From 79a169f53e3bc5164292e17bc159737293207812 Mon Sep 17 00:00:00 2001 From: Telokis <6382729+Telokis@users.noreply.github.com> Date: Sun, 11 Aug 2024 15:18:11 +0200 Subject: [PATCH 01/75] Add initial docker-compose.yaml for Pterodactyl --- public/svgs/pterodactyl.png | Bin 0 -> 32004 bytes templates/compose/pterodactyl.yaml | 157 +++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 public/svgs/pterodactyl.png create mode 100644 templates/compose/pterodactyl.yaml diff --git a/public/svgs/pterodactyl.png b/public/svgs/pterodactyl.png new file mode 100644 index 0000000000000000000000000000000000000000..a5addb87c5cf70b426a5a13a563a677088cad3a3 GIT binary patch literal 32004 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

MZBCd6fS^X@TN}n= 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 Ke1)t<@c#oi$r%~| literal 0 HcmV?d00001 diff --git a/templates/compose/pterodactyl.yaml b/templates/compose/pterodactyl.yaml new file mode 100644 index 000000000..436065a40 --- /dev/null +++ b/templates/compose/pterodactyl.yaml @@ -0,0 +1,157 @@ +# documentation: https://pterodactyl.io/ +# slogan: Pterodactyl is a free, open-source game server management panel +# tags: game, game server, management, panel, minecraft +# logo: svgs/pterodactyl.png +# port: 80 + +services: + mariadb: + image: mariadb:10.5 + restart: unless-stopped + command: --default-authentication-plugin=mysql_native_password + healthcheck: + test: + ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized || exit 1"] + start_period: 10s + interval: 10s + timeout: 1s + retries: 3 + environment: + - SERVICE_PASSWORD_MYSQL + - MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_MYSQLROOT + - MYSQL_DATABASE=panel + - MYSQL_USER=pterodactyl + - MYSQL_PASSWORD=$SERVICE_PASSWORD_MYSQL + volumes: + - pterodactyl-db:/var/lib/mysql + + redis: + image: redis:alpine + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "redis-cli ping || exit 1"] + interval: 10s + timeout: 1s + retries: 3 + + pterodactyl: + image: ghcr.io/pterodactyl/panel:latest + restart: unless-stopped + volumes: + - "panel-var:/app/var/" + - "panel-nginx:/etc/nginx/http.d/" + - "panel-certs:/etc/letsencrypt/" + - "panel-logs:/app/storage/logs" + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:80 || exit 1"] + interval: 10s + timeout: 1s + retries: 3 + environment: + - SERVICE_FQDN_PTERODACTYL + + - APP_ENV=production + - APP_ENVIRONMENT_ONLY=false + - APP_URL=${PTERODACTYL_PUBLIC_FQDN:-$SERVICE_FQDN_PTERODACTYL} + - APP_TIMEZONE=${TIMEZONE:-UTC} + - APP_SERVICE_AUTHOR=$APP_SERVICE_AUTHOR + - LOG_LEVEL=${LOG_LEVEL:-debug} + + - CACHE_DRIVER=redis + - SESSION_DRIVER=redis + - QUEUE_DRIVER=redis + - REDIS_HOST=redis + + - DB_HOST=mariadb + - DB_PORT=3306 + - DB_PASSWORD=$SERVICE_PASSWORD_MYSQL + + - MAIL_FROM=$MAIL_FROM + - MAIL_DRIVER=$MAIL_DRIVER + - MAIL_HOST=$MAIL_HOST + - MAIL_PORT=$MAIL_PORT + - MAIL_USERNAME=$MAIL_USERNAME + - MAIL_PASSWORD=$MAIL_PASSWORD + - MAIL_ENCRYPTION=$MAIL_ENCRYPTION + + wings: + image: ghcr.io/pterodactyl/wings:latest + restart: unless-stopped + environment: + - TZ=${TIMEZONE:-UTC} + - WINGS_USERNAME=pterodactyl + healthcheck: + test: ["CMD", "curl", "-sf http://localhost:8080"] + interval: 10s + timeout: 1s + retries: 3 + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "/var/lib/docker/containers/:/var/lib/docker/containers/" + - "wings-lib:/var/lib/pterodactyl/" + - "wings-logs:/var/log/pterodactyl/" + + - type: bind + source: ./etc/config.yml + target: /etc/pterodactyl/config.yml + content: | + docker: + network: + interface: 172.28.0.1 + dns: + - 1.1.1.1 + - 1.0.0.1 + name: pterodactyl_nw + ispn: false + driver: "" + network_mode: pterodactyl_nw + is_internal: false + enable_icc: true + network_mtu: 1500 + interfaces: + v4: + subnet: 172.28.0.0/16 + gateway: 172.28.0.1 + v6: + subnet: fdba:17c8:6c94::/64 + gateway: fdba:17c8:6c94::1011 + +volumes: + panel-var: + panel-nginx: + panel-certs: + panel-logs: + wings-lib: + wings-logs: + pterodactyl-db: +# Instructions: +# - Wait for the Pterodactyl service to be healthy (can take a few minutes) +# - Use the command `php artisan p:user:make --no-interaction --admin=1 --email= --username= --name-first= --name-last= --password=` in the Pterodactyl container to create an admin user. +# - Login to the panel using the created user +# - Go to the Admin panel (Cog icon at the top) +# - Go to "Locations" on the left +# - Create a new location +# - Go to "Nodes" on the left +# - Create a new node +# - Specify a temporary FQDN like "localhost" +# - /!\ Check "Use HTTP Connection" /!\ +# - Go to the "Configuration" tab of your newly created node +# - Copy the configuration file +# - In Coolify go to the "Storage" menu for your resource. +# - Find the big text area associated with `config.yml` for Wings +# - Paste the configuration at the top of this file, above `docker:` +# - Edit the line `remote: ''` to `remote: 'http://pterodactyl'` +# - Save the file +# - On the Pterodactyl panel for the node, go to the "Settings" tab +# - Change the "Fully Qualified Domain Name" from `localhost` to `wings` +# - Go to the "About" tab and confirm that the "Information" section shows the "Daemon Version" properly. + +# +----------+--------------------------------------+ +# | Field | Value | +# +----------+--------------------------------------+ +# | UUID | 6b3083ca-274b-4a77-b88f-6fbf5e4f286f | +# | Email | telokis@example.com | +# | Username | telokis | +# | Name | Telo Kis | +# | Admin | Yes | +# +----------+--------------------------------------+ From de6be8c840f7737a6f51e8cd5f620f0e6c47fecc Mon Sep 17 00:00:00 2001 From: Telokis <6382729+Telokis@users.noreply.github.com> Date: Sun, 11 Aug 2024 21:06:24 +0200 Subject: [PATCH 02/75] Fix a lot of small mistakes --- templates/compose/pterodactyl.yaml | 45 +++--------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/templates/compose/pterodactyl.yaml b/templates/compose/pterodactyl.yaml index 436065a40..19315b4ce 100644 --- a/templates/compose/pterodactyl.yaml +++ b/templates/compose/pterodactyl.yaml @@ -41,7 +41,6 @@ services: - "panel-var:/app/var/" - "panel-nginx:/etc/nginx/http.d/" - "panel-certs:/etc/letsencrypt/" - - "panel-logs:/app/storage/logs" healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:80 || exit 1"] interval: 10s @@ -54,7 +53,7 @@ services: - APP_ENVIRONMENT_ONLY=false - APP_URL=${PTERODACTYL_PUBLIC_FQDN:-$SERVICE_FQDN_PTERODACTYL} - APP_TIMEZONE=${TIMEZONE:-UTC} - - APP_SERVICE_AUTHOR=$APP_SERVICE_AUTHOR + - APP_SERVICE_AUTHOR=${APP_SERVICE_AUTHOR:-author@example.com} - LOG_LEVEL=${LOG_LEVEL:-debug} - CACHE_DRIVER=redis @@ -78,17 +77,14 @@ services: image: ghcr.io/pterodactyl/wings:latest restart: unless-stopped environment: + - SERVICE_FQDN_WINGS_8080 - TZ=${TIMEZONE:-UTC} - WINGS_USERNAME=pterodactyl - healthcheck: - test: ["CMD", "curl", "-sf http://localhost:8080"] - interval: 10s - timeout: 1s - retries: 3 volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "/var/lib/docker/containers/:/var/lib/docker/containers/" - - "wings-lib:/var/lib/pterodactyl/" + - "/var/lib/pterodactyl/:/var/lib/pterodactyl/" # See https://discord.com/channels/122900397965705216/493443725012500490/1272195151309045902 + - "/tmp/pterodactyl/:/tmp/pterodactyl/" # See https://discord.com/channels/122900397965705216/493443725012500490/1272195151309045902 - "wings-logs:/var/log/pterodactyl/" - type: bind @@ -120,38 +116,5 @@ volumes: panel-var: panel-nginx: panel-certs: - panel-logs: - wings-lib: wings-logs: pterodactyl-db: -# Instructions: -# - Wait for the Pterodactyl service to be healthy (can take a few minutes) -# - Use the command `php artisan p:user:make --no-interaction --admin=1 --email= --username= --name-first= --name-last= --password=` in the Pterodactyl container to create an admin user. -# - Login to the panel using the created user -# - Go to the Admin panel (Cog icon at the top) -# - Go to "Locations" on the left -# - Create a new location -# - Go to "Nodes" on the left -# - Create a new node -# - Specify a temporary FQDN like "localhost" -# - /!\ Check "Use HTTP Connection" /!\ -# - Go to the "Configuration" tab of your newly created node -# - Copy the configuration file -# - In Coolify go to the "Storage" menu for your resource. -# - Find the big text area associated with `config.yml` for Wings -# - Paste the configuration at the top of this file, above `docker:` -# - Edit the line `remote: ''` to `remote: 'http://pterodactyl'` -# - Save the file -# - On the Pterodactyl panel for the node, go to the "Settings" tab -# - Change the "Fully Qualified Domain Name" from `localhost` to `wings` -# - Go to the "About" tab and confirm that the "Information" section shows the "Daemon Version" properly. - -# +----------+--------------------------------------+ -# | Field | Value | -# +----------+--------------------------------------+ -# | UUID | 6b3083ca-274b-4a77-b88f-6fbf5e4f286f | -# | Email | telokis@example.com | -# | Username | telokis | -# | Name | Telo Kis | -# | Admin | Yes | -# +----------+--------------------------------------+ From 81e9c6d8a3c02229bef75f586acadfc357a1400f Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:26:28 +0100 Subject: [PATCH 03/75] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a3ce0132..e834ecb34 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ By subscribing to the cloud version, you get the Coolify server for the same pri | Andras Bacsai | Peak | |------------|------------| | Andras Bacsai | Peak Labs | -| | | +| | | # Repo Activity From 8a5a67813c3cc48d21bbbcf999199b3d8d72d168 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:29:02 +0100 Subject: [PATCH 04/75] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e834ecb34..a3ea4a272 100644 --- a/README.md +++ b/README.md @@ -147,10 +147,10 @@ By subscribing to the cloud version, you get the Coolify server for the same pri # Core Maintainers -| Andras Bacsai | Peak | +| Andras Bacsai | 🏔️ Peak | |------------|------------| | Andras Bacsai | Peak Labs | -| | | +| | | # Repo Activity From cfbe21feccbfb815feefb8034f1d4de38b842790 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:29:30 +0100 Subject: [PATCH 05/75] Revert "Update README.md" This reverts commit 8a5a67813c3cc48d21bbbcf999199b3d8d72d168. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a3ea4a272..e834ecb34 100644 --- a/README.md +++ b/README.md @@ -147,10 +147,10 @@ By subscribing to the cloud version, you get the Coolify server for the same pri # Core Maintainers -| Andras Bacsai | 🏔️ Peak | +| Andras Bacsai | Peak | |------------|------------| | Andras Bacsai | Peak Labs | -| | | +| | | # Repo Activity From 6497514f4ebab60959627cbac04d7378700d8119 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:29:52 +0100 Subject: [PATCH 06/75] Reapply "Update README.md" This reverts commit cfbe21feccbfb815feefb8034f1d4de38b842790. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e834ecb34..a3ea4a272 100644 --- a/README.md +++ b/README.md @@ -147,10 +147,10 @@ By subscribing to the cloud version, you get the Coolify server for the same pri # Core Maintainers -| Andras Bacsai | Peak | +| Andras Bacsai | 🏔️ Peak | |------------|------------| | Andras Bacsai | Peak Labs | -| | | +| | | # Repo Activity From e97a122d6360beff054e7875c75f4f447487378e Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:34:42 +0100 Subject: [PATCH 07/75] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3ea4a272..8f0df0bda 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ By subscribing to the cloud version, you get the Coolify server for the same pri | Andras Bacsai | 🏔️ Peak | |------------|------------| -| Andras Bacsai | Peak Labs | +| Andras Bacsai | peaklabs-dev | | | | # Repo Activity From 1f499c148867295b1db3f329330c01d7e4d3fe8a Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Sat, 23 Nov 2024 13:04:54 +0530 Subject: [PATCH 08/75] Add no encryption option for SMTP settings Related to #4311 Add option to configure SMTP settings without encryption. * Update `app/Livewire/Notifications/Email.php` and `app/Livewire/SettingsEmail.php` to include "No Encryption" option in the `smtpEncryption` field and update validation rules. * Modify `app/Notifications/Channels/EmailChannel.php` to handle the "No Encryption" option in the `bootConfigs` method. * Add `set_transanctional_email_settings` function in `app/Livewire/Help.php` to support the "No Encryption" option. * Update `config/mail.php` to handle the "No Encryption" option in the mail configuration. --- app/Livewire/Help.php | 32 +++++++++++++++++++++ app/Livewire/Notifications/Email.php | 2 +- app/Livewire/SettingsEmail.php | 2 +- app/Notifications/Channels/EmailChannel.php | 2 +- config/mail.php | 2 +- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index f51527fbe..aa354e94e 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -56,3 +56,35 @@ class Help extends Component return view('livewire.help')->layout('layouts.app'); } } + +function set_transanctional_email_settings($settings = null) +{ + if (is_null($settings)) { + $settings = instanceSettings(); + } + + if ($settings->resend_enabled) { + config()->set('mail.default', 'resend'); + config()->set('resend.api_key', $settings->resend_api_key); + + return 'resend'; + } + + if ($settings->smtp_enabled) { + config()->set('mail.default', 'smtp'); + config()->set('mail.mailers.smtp', [ + 'transport' => 'smtp', + 'host' => $settings->smtp_host, + 'port' => $settings->smtp_port, + 'encryption' => $settings->smtp_encryption === 'none' ? null : $settings->smtp_encryption, + 'username' => $settings->smtp_username, + 'password' => $settings->smtp_password, + 'timeout' => $settings->smtp_timeout, + 'local_domain' => null, + ]); + + return 'smtp'; + } + + return false; +} diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index fcedf1305..682180aa8 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -37,7 +37,7 @@ class Email extends Component #[Validate(['nullable', 'numeric'])] public ?int $smtpPort = null; - #[Validate(['nullable', 'string'])] + #[Validate(['nullable', 'string', 'in:tls,ssl,none'])] public ?string $smtpEncryption = null; #[Validate(['nullable', 'string'])] diff --git a/app/Livewire/SettingsEmail.php b/app/Livewire/SettingsEmail.php index 61f720b3a..abf3a12f9 100644 --- a/app/Livewire/SettingsEmail.php +++ b/app/Livewire/SettingsEmail.php @@ -19,7 +19,7 @@ class SettingsEmail extends Component #[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])] public ?int $smtpPort = null; - #[Validate(['nullable', 'string'])] + #[Validate(['nullable', 'string', 'in:tls,ssl,none'])] public ?string $smtpEncryption = null; #[Validate(['nullable', 'string'])] diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php index af9af978d..e745990e4 100644 --- a/app/Notifications/Channels/EmailChannel.php +++ b/app/Notifications/Channels/EmailChannel.php @@ -66,7 +66,7 @@ class EmailChannel 'transport' => 'smtp', 'host' => data_get($notifiable, 'smtp_host'), 'port' => data_get($notifiable, 'smtp_port'), - 'encryption' => data_get($notifiable, 'smtp_encryption'), + 'encryption' => data_get($notifiable, 'smtp_encryption') === 'none' ? null : data_get($notifiable, 'smtp_encryption'), 'username' => data_get($notifiable, 'smtp_username'), 'password' => data_get($notifiable, 'smtp_password'), 'timeout' => data_get($notifiable, 'smtp_timeout'), diff --git a/config/mail.php b/config/mail.php index 26af507d9..b36bd363c 100644 --- a/config/mail.php +++ b/config/mail.php @@ -38,7 +38,7 @@ return [ 'transport' => 'smtp', 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 'port' => env('MAIL_PORT', 587), - 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'encryption' => env('MAIL_ENCRYPTION', 'tls') === 'none' ? null : env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'timeout' => null, From f45411f3d6f869ea52ccc0022a28856e31830cc5 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Sat, 23 Nov 2024 13:08:53 +0530 Subject: [PATCH 09/75] Fix metrics error when data is less than selected date Related to #4309 Fix the 'sql: Scan error on column index 5, name "usedPercent": converting NULL to float64 is unsupported' error on the metrics page. * Update the `getMemoryMetrics` method in `app/Models/Server.php` to handle NULL values for the "usedPercent" field by setting them to 0.0. * Add a check for NULL values in the `getMemoryMetrics` method before converting to float64. --- app/Models/Server.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 5bbd13ac7..1e872227a 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -611,7 +611,8 @@ $schema://$host { } $memory = json_decode($memory, true); $parsedCollection = collect($memory)->map(function ($metric) { - return [(int) $metric['time'], (float) $metric['usedPercent']]; + $usedPercent = $metric['usedPercent'] ?? 0.0; + return [(int) $metric['time'], (float) $usedPercent]; }); return $parsedCollection->toArray(); From a836d78f0bb7716134f2424d917636046b41e91a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 25 Nov 2024 14:07:50 +0100 Subject: [PATCH 10/75] remove unnecessary function --- app/Livewire/Help.php | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index aa354e94e..f51527fbe 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -56,35 +56,3 @@ class Help extends Component return view('livewire.help')->layout('layouts.app'); } } - -function set_transanctional_email_settings($settings = null) -{ - if (is_null($settings)) { - $settings = instanceSettings(); - } - - if ($settings->resend_enabled) { - config()->set('mail.default', 'resend'); - config()->set('resend.api_key', $settings->resend_api_key); - - return 'resend'; - } - - if ($settings->smtp_enabled) { - config()->set('mail.default', 'smtp'); - config()->set('mail.mailers.smtp', [ - 'transport' => 'smtp', - 'host' => $settings->smtp_host, - 'port' => $settings->smtp_port, - 'encryption' => $settings->smtp_encryption === 'none' ? null : $settings->smtp_encryption, - 'username' => $settings->smtp_username, - 'password' => $settings->smtp_password, - 'timeout' => $settings->smtp_timeout, - 'local_domain' => null, - ]); - - return 'smtp'; - } - - return false; -} From e8a0bd37f428cc6b48982b4db8f4d6e250327f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Tue, 26 Nov 2024 13:47:50 -0500 Subject: [PATCH 11/75] added plex logo --- public/svgs/plex.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/svgs/plex.svg diff --git a/public/svgs/plex.svg b/public/svgs/plex.svg new file mode 100644 index 000000000..872b135cf --- /dev/null +++ b/public/svgs/plex.svg @@ -0,0 +1 @@ + \ No newline at end of file From c10de805fa9ae9e6f5a56eda4f0baaf4642744bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Tue, 26 Nov 2024 13:47:58 -0500 Subject: [PATCH 12/75] first try adding ple --- templates/compose/plex.yaml | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 templates/compose/plex.yaml diff --git a/templates/compose/plex.yaml b/templates/compose/plex.yaml new file mode 100644 index 000000000..740b10c66 --- /dev/null +++ b/templates/compose/plex.yaml @@ -0,0 +1,39 @@ +# documentation: https://github.com/plexinc/pms-docker +# slogan: Plex combines free movies & TV with the best free streaming services, so there’s always more to discover. +# tags: media, server, movies, tv, music +# logo: svgs/plex.svg +# port: 32400 + +services: + plex: + image: lscr.io/linuxserver/plex:latest + ports: + - 32400:32400 + - 1900:1900/udp + - 5353:5353/udp + - 8324:8324 + - 32410:32410/udp + - 32412:32412/udp + - 32413:32413/udp + - 32414:32414/udp + - 32469:32469 + environment: + - SERVICE_FQDN_PLEX + - _APP_URL=$SERVICE_FQDN_PLEX + - SERVICE_FQDN_PLEX_32400 + - PUID=1000 + - PGID=1000 + - TZ=Europe/Madrid + - VERSION=latest + - PLEX_CLAIM=${PLEX_CLAIM} + devices: + - "/dev/dri:/dev/dri" + volumes: + - plex-config:/config + - plex-tvshows:/data/tvshows + - plex-movies:/data/movies + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:32400"] + interval: 2s + timeout: 10s + retries: 15 From 63b8dacd064d12fdd9879eb5182c7c555358902a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Tue, 26 Nov 2024 14:37:21 -0500 Subject: [PATCH 13/75] working config with claim token --- templates/compose/plex.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/templates/compose/plex.yaml b/templates/compose/plex.yaml index 740b10c66..23df694bc 100644 --- a/templates/compose/plex.yaml +++ b/templates/compose/plex.yaml @@ -6,7 +6,7 @@ services: plex: - image: lscr.io/linuxserver/plex:latest + image: plexinc/pms-docker:latest ports: - 32400:32400 - 1900:1900/udp @@ -18,13 +18,11 @@ services: - 32414:32414/udp - 32469:32469 environment: - - SERVICE_FQDN_PLEX - - _APP_URL=$SERVICE_FQDN_PLEX - SERVICE_FQDN_PLEX_32400 + - _APP_URL=$SERVICE_FQDN_PLEX - PUID=1000 - PGID=1000 - - TZ=Europe/Madrid - - VERSION=latest + - TZ=${TZ:-America/Toronto} - PLEX_CLAIM=${PLEX_CLAIM} devices: - "/dev/dri:/dev/dri" @@ -33,7 +31,7 @@ services: - plex-tvshows:/data/tvshows - plex-movies:/data/movies healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:32400"] + test: ["CMD", "curl", "-f", "http://localhost:32400/identity"] interval: 2s timeout: 10s retries: 15 From 29217ff03f48e4b0218eac48f21398c70573723e Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:33:24 +0100 Subject: [PATCH 14/75] fix: postiz service For some reason, Postiz hardcodes username for Postgres and Redis in their code, so we have to use their usernames to make it work. --- templates/compose/postiz.yaml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/templates/compose/postiz.yaml b/templates/compose/postiz.yaml index 278bd12c6..503d0f67e 100644 --- a/templates/compose/postiz.yaml +++ b/templates/compose/postiz.yaml @@ -13,9 +13,8 @@ services: - FRONTEND_URL=${SERVICE_FQDN_POSTIZ} - NEXT_PUBLIC_BACKEND_URL=${SERVICE_FQDN_POSTIZ}/api - JWT_SECRET=${SERVICE_PASSWORD_JWTSECRET} - - DATABASE_URL=postgresql://${SERVICE_USER_POSTGRESQL}:${SERVICE_PASSWORD_POSTGRESQL}@postgres:5432/${POSTGRESQL_DATABASE:-postiz-db} - # Changed Redis URL to use default username - - REDIS_URL=redis://${SERVICE_USER_REDIS}:${SERVICE_PASSWORD_REDIS}@redis:6379 + - DATABASE_URL=postgresql://postgres:${SERVICE_PASSWORD_POSTGRESQL}@postgres:5432/${POSTGRESQL_DATABASE:-postiz-db} + - REDIS_URL=redis://default:${SERVICE_PASSWORD_REDIS}@redis:6379 - BACKEND_INTERNAL_URL=http://localhost:3000 # Cloudflare R2 Settings @@ -75,7 +74,7 @@ services: # Misc Settings - NEXT_PUBLIC_DISCORD_SUPPORT=${NEXT_PUBLIC_DISCORD_SUPPORT} - NEXT_PUBLIC_POLOTNO=${NEXT_PUBLIC_POLOTNO} - - IS_GENERAL=${IS_GENERAL:-true} + - IS_GENERAL=true - NX_ADD_PLUGINS=${NX_ADD_PLUGINS:-false} # Payment Settings @@ -106,13 +105,11 @@ services: volumes: - postiz_postgresql_data:/var/lib/postgresql/data environment: - - POSTGRES_USER=${SERVICE_USER_POSTGRESQL} + - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL} - POSTGRES_DB=${POSTGRESQL_DATABASE:-postiz-db} healthcheck: - test: - - CMD-SHELL - - pg_isready -U ${SERVICE_USER_POSTGRESQL} -d ${POSTGRESQL_DATABASE:-postiz-db} + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB:-postiz-db}"] interval: 5s timeout: 20s retries: 10 @@ -121,7 +118,6 @@ services: image: redis:7.2 environment: - REDIS_PASSWORD=${SERVICE_PASSWORD_REDIS} - - REDIS_USER=${SERVICE_USER_REDIS} command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS} volumes: - postiz_redis_data:/data From b08a1ef3f188d797efcb28db55b50a344068a615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Wed, 27 Nov 2024 08:42:02 -0500 Subject: [PATCH 15/75] removed optional ports --- templates/compose/plex.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/templates/compose/plex.yaml b/templates/compose/plex.yaml index 23df694bc..8d55ed5bc 100644 --- a/templates/compose/plex.yaml +++ b/templates/compose/plex.yaml @@ -7,16 +7,6 @@ services: plex: image: plexinc/pms-docker:latest - ports: - - 32400:32400 - - 1900:1900/udp - - 5353:5353/udp - - 8324:8324 - - 32410:32410/udp - - 32412:32412/udp - - 32413:32413/udp - - 32414:32414/udp - - 32469:32469 environment: - SERVICE_FQDN_PLEX_32400 - _APP_URL=$SERVICE_FQDN_PLEX From e7e00e230647d835a8de0bd3573ced82ee77cfb9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 27 Nov 2024 14:49:47 +0100 Subject: [PATCH 16/75] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f0df0bda..dac48d127 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Special thanks to our biggest sponsors! ### Special Sponsors -![image](https://github.com/user-attachments/assets/152bd1e0-e0c1-4d47-8a4f-0eb3700d2e61) +![image](https://github.com/user-attachments/assets/726fb63e-c3b8-4260-b3ac-06780605ec5d) * [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. @@ -50,6 +50,7 @@ Special thanks to our biggest sponsors! * [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. +* [GoldenVM](https://billing.goldenvm.com/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes. * [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. * [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. @@ -90,7 +91,6 @@ Special thanks to our biggest sponsors! Paweł Pierścionek Michael Mazurczak Formbricks -Adith Suhas StartupFame Jonas Jaeger JP From daa2700b1c4679637206f52502476240e79e1c79 Mon Sep 17 00:00:00 2001 From: Luna Midori <118759930+lunamidori5@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:37:18 -0800 Subject: [PATCH 17/75] Added support for Endeavour OS as arch --- scripts/install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/install.sh b/scripts/install.sh index 3f1bec0c4..1a039f64f 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -83,6 +83,11 @@ if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then OS_TYPE="arch" fi +# Check if the OS is Endeavour OS, if so, change it to arch +if [ "$OS_TYPE" = "endeavouros" ]; then + OS_TYPE="arch" +fi + # Check if the OS is Asahi Linux, if so, change it to fedora if [ "$OS_TYPE" = "fedora-asahi-remix" ]; then OS_TYPE="fedora" From 0bc823108dfd02a0cddc97b46b74a9f5b4c7e8c8 Mon Sep 17 00:00:00 2001 From: kleintonno <116087195+kleintonno@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:40:44 +0100 Subject: [PATCH 18/75] fix deploy selection in applications source menu --- resources/views/livewire/project/application/source.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/project/application/source.blade.php b/resources/views/livewire/project/application/source.blade.php index 4095c63e0..4aab498e9 100644 --- a/resources/views/livewire/project/application/source.blade.php +++ b/resources/views/livewire/project/application/source.blade.php @@ -43,7 +43,7 @@

Select another Private Key

@foreach ($privateKeys as $key) - {{ $key->name }} + {{ $key->name }} @endforeach
From 64ac17926245e60eb34ebdd19adb80c4807c38a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Thu, 28 Nov 2024 07:54:54 -0500 Subject: [PATCH 19/75] added overseer --- public/svgs/overseerr.svg | 15 +++++++++++++++ templates/compose/overseerr.yaml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 public/svgs/overseerr.svg create mode 100644 templates/compose/overseerr.yaml diff --git a/public/svgs/overseerr.svg b/public/svgs/overseerr.svg new file mode 100644 index 000000000..030df6a82 --- /dev/null +++ b/public/svgs/overseerr.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/compose/overseerr.yaml b/templates/compose/overseerr.yaml new file mode 100644 index 000000000..8bf3a8666 --- /dev/null +++ b/templates/compose/overseerr.yaml @@ -0,0 +1,30 @@ +# documentation: https://docs.overseerr.dev/getting-started/installation#docker +# slogan: Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem. +# tags: media,request,plex,sonarr,radarr +# logo: svgs/overseerr.svg +# port: 5055 + +services: + overseerr: + image: sctx/overseerr:latest + environment: + - SERVICE_FQDN_OVERSEERR_5055 + - PUID=1000 + - PGID=1000 + - TZ=${TZ:-America/Toronto} + volumes: + - overseerr-config:/app/config + healthcheck: + test: + [ + "CMD", + "wget", + "--tries", + "1", + "--no-verbose", + "--spider", + "http://localhost:5055/api/v1/status", + ] + interval: 2s + timeout: 10s + retries: 15 From 5bc0e24d51cd4e5d4168818a437f703a9ca191a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Thu, 28 Nov 2024 08:03:06 -0500 Subject: [PATCH 20/75] fixed svg size --- public/svgs/overseerr.svg | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/public/svgs/overseerr.svg b/public/svgs/overseerr.svg index 030df6a82..8116787c2 100644 --- a/public/svgs/overseerr.svg +++ b/public/svgs/overseerr.svg @@ -1,15 +1,6 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + From 246b137e6124b8c37afb42de3babc61ab904a1df Mon Sep 17 00:00:00 2001 From: Darren Sisson Date: Fri, 29 Nov 2024 14:07:47 +0000 Subject: [PATCH 21/75] fix formbricks template --- templates/compose/formbricks.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/compose/formbricks.yaml b/templates/compose/formbricks.yaml index b0729284a..dde216b80 100644 --- a/templates/compose/formbricks.yaml +++ b/templates/compose/formbricks.yaml @@ -12,10 +12,10 @@ services: - WEBAPP_URL=$SERVICE_FQDN_FORMBRICKS - DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgresql:5432/${POSTGRESQL_DATABASE:-formbricks} - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL} - - NEXTAUTH_SECRET=$SERVICE_BASE64_64_NEXTAUTH + - NEXTAUTH_SECRET=$SERVICE_BASE64_NEXTAUTH - NEXTAUTH_URL=$SERVICE_FQDN_FORMBRICKS - - ENCRYPTION_KEY=$SERVICE_BASE64_64_ENCRYPTION - - CRON_SECRET=$SERVICE_BASE64_64_CRON + - ENCRYPTION_KEY=$SERVICE_BASE64_ENCRYPTION + - CRON_SECRET=$SERVICE_BASE64_CRON - ENTERPRISE_LICENSE_KEY=${ENTERPRISE_LICENSE_KEY} - MAIL_FROM=${MAIL_FROM:-test@example.com} - SMTP_HOST=${SMTP_HOST:-test.example.com} @@ -59,7 +59,7 @@ services: - REDIS_URL=${REDIS_URL} - REDIS_HTTP_URL=${REDIS_HTTP_URL} - DEFAULT_ORGANIZATION_ID=${DEFAULT_ORGANIZATION_ID} - - DEFAULT_ORGANIZATION_ROLE=${DEFAULT_ORGANIZATION_ROLE:-admin} + - DEFAULT_ORGANIZATION_ROLE=${DEFAULT_ORGANIZATION_ROLE:-owner} volumes: - formbricks-uploads:/apps/web/uploads/ depends_on: @@ -72,7 +72,7 @@ services: retries: 15 postgresql: - image: postgres:16-alpine + image: pgvector/pgvector:pg16 volumes: - formbricks-postgresql-data:/var/lib/postgresql/data environment: From eb3f2442bb3f121b861dce2126518b1908e40c82 Mon Sep 17 00:00:00 2001 From: Darren Sisson Date: Fri, 29 Nov 2024 15:57:48 +0000 Subject: [PATCH 22/75] fix budibase template --- templates/compose/budibase.yaml | 45 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/templates/compose/budibase.yaml b/templates/compose/budibase.yaml index 516fefd68..a5ecb8fbb 100644 --- a/templates/compose/budibase.yaml +++ b/templates/compose/budibase.yaml @@ -9,11 +9,11 @@ services: image: budibase.docker.scarf.sh/budibase/apps environment: - SELF_HOSTED=1 - - COUCH_DB_URL=http://$SERVICE_USER_BUDIBASE_COUCHDB:$SERVICE_PASSWORD_BUDIBASE_COUCHDB@couchdb-service:5984 + - COUCH_DB_URL=http://$SERVICE_USER_COUCHDB:$SERVICE_PASSWORD_COUCHDB@couchdb-service:5984 - WORKER_URL=http://worker-service:4003 - MINIO_URL=http://minio-service:9000 - - MINIO_ACCESS_KEY=$SERVICE_USER_BUDIBASE_MINIO - - MINIO_SECRET_KEY=$SERVICE_PASSWORD_BUDIBASE_MINIO + - MINIO_ACCESS_KEY=$SERVICE_USER_MINIO + - MINIO_SECRET_KEY=$SERVICE_PASSWORD_MINIO - INTERNAL_API_KEY=$SERVICE_BASE64_128_BUDIBASE - BUDIBASE_ENVIRONMENT=${BUDIBASE_ENVIRONMENT:-PRODUCTION} - PORT=4002 @@ -22,14 +22,14 @@ services: - LOG_LEVEL=info - ENABLE_ANALYTICS=${ENABLE_ANALYTICS:-true} - REDIS_URL=redis-service:6379 - - REDIS_PASSWORD=$SERVICE_PASSWORD_BUDIBASE_REDIS + - REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS - BB_ADMIN_USER_EMAIL= - BB_ADMIN_USER_PASSWORD= depends_on: - worker-service - redis-service healthcheck: - test: ["CMD", "curl", "-f", "http://app-service:4002"] + test: ["CMD", "wget", "--spider", "-qO-", "http://localhost:4002/health"] interval: 15s timeout: 15s retries: 5 @@ -43,21 +43,21 @@ services: - CLUSTER_PORT=10000 - API_ENCRYPTION_KEY=$SERVICE_BASE64_64_BUDIBASE - JWT_SECRET=$SERVICE_BASE64_64_BUDIBASE - - MINIO_ACCESS_KEY=$SERVICE_USER_BUDIBASE_MINIO - - MINIO_SECRET_KEY=$SERVICE_PASSWORD_BUDIBASE_MINIO + - MINIO_ACCESS_KEY=$SERVICE_USER_MINIO + - MINIO_SECRET_KEY=$SERVICE_PASSWORD_MINIO - MINIO_URL=http://minio-service:9000 - APPS_URL=http://app-service:4002 - - COUCH_DB_USERNAME=$SERVICE_USER_BUDIBASE_COUCHDB - - COUCH_DB_PASSWORD=$SERVICE_PASSWORD_BUDIBASE_COUCHDB - - COUCH_DB_URL=http://$SERVICE_USER_BUDIBASE_COUCHDB:$SERVICE_PASSWORD_BUDIBASE_COUCHDB@couchdb-service:5984 + - COUCH_DB_USERNAME=$SERVICE_USER_COUCHDB + - COUCH_DB_PASSWORD=$SERVICE_PASSWORD_COUCHDB + - COUCH_DB_URL=http://$SERVICE_USER_COUCHDB:$SERVICE_PASSWORD_COUCHDB@couchdb-service:5984 - INTERNAL_API_KEY=$SERVICE_BASE64_128_BUDIBASE - REDIS_URL=redis-service:6379 - - REDIS_PASSWORD=$SERVICE_PASSWORD_BUDIBASE_REDIS + - REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS depends_on: - redis-service - minio-service healthcheck: - test: ["CMD", "curl", "-f", "http://worker-service:4003"] + test: ["CMD", "wget", "--spider", "-qO-", "http://localhost:4003/health"] interval: 15s timeout: 15s retries: 5 @@ -68,8 +68,8 @@ services: volumes: - minio_data:/data environment: - - MINIO_ACCESS_KEY=$SERVICE_USER_BUDIBASE_MINIO - - MINIO_SECRET_KEY=$SERVICE_PASSWORD_BUDIBASE_MINIO + - MINIO_ROOT_USER=$SERVICE_USER_MINIO + - MINIO_ROOT_PASSWORD=$SERVICE_PASSWORD_MINIO - MINIO_BROWSER=off command: server /data --console-address ":9001" healthcheck: @@ -105,8 +105,8 @@ services: couchdb-service: image: budibase/couchdb environment: - - COUCHDB_PASSWORD=$SERVICE_PASSWORD_BUDIBASE_COUCHDB - - COUCHDB_USER=$SERVICE_USER_BUDIBASE_COUCHDB + - COUCHDB_PASSWORD=$SERVICE_PASSWORD_COUCHDB + - COUCHDB_USER=$SERVICE_USER_COUCHDB - TARGETBUILD=docker-compose healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5984/"] @@ -119,12 +119,12 @@ services: redis-service: image: redis - command: redis-server --requirepass "$SERVICE_PASSWORD_BUDIBASE_REDIS" + command: redis-server --requirepass "$SERVICE_PASSWORD_REDIS" volumes: - redis_data:/data healthcheck: test: - ["CMD", "redis-cli", "-a", "$SERVICE_PASSWORD_BUDIBASE_REDIS", "ping"] + ["CMD", "redis-cli", "-a", "$SERVICE_PASSWORD_REDIS", "ping"] interval: 15s timeout: 15s retries: 5 @@ -138,13 +138,8 @@ services: command: --debug --http-api-update bbapps bbworker bbproxy environment: - WATCHTOWER_HTTP_API=true - - WATCHTOWER_HTTP_API_TOKEN=$SERVICE_PASSWORD_BUDIBASE_WATCHTOWER + - WATCHTOWER_HTTP_API_TOKEN=$SERVICE_PASSWORD_WATCHTOWER - WATCHTOWER_CLEANUP=true labels: - "com.centurylinklabs.watchtower.enable=false" - healthcheck: - test: ["CMD", "curl", "-f", "http://watchtower-service:8080"] - interval: 15s - timeout: 15s - retries: 5 - start_period: 10s + exclude_from_hc: true From b3f968db76193f62dc51f4bb2bf996358441cc17 Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sun, 1 Dec 2024 10:15:45 +1100 Subject: [PATCH 23/75] fix: missing `mysql_password` API property --- app/Http/Controllers/Api/DatabasesController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index ce658d2a2..21816557f 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -213,6 +213,7 @@ class DatabasesController extends Controller 'mongo_initdb_root_password' => ['type' => 'string', 'description' => 'Mongo initdb root password'], 'mongo_initdb_init_database' => ['type' => 'string', 'description' => 'Mongo initdb init database'], 'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'], + 'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'], 'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'], 'mysql_database' => ['type' => 'string', 'description' => 'MySQL database'], 'mysql_conf' => ['type' => 'string', 'description' => 'MySQL conf'], @@ -443,9 +444,10 @@ class DatabasesController extends Controller break; case 'standalone-mysql': - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $validator = customApiValidator($request->all(), [ 'mysql_root_password' => 'string', + 'mysql_password' => 'string', 'mysql_user' => 'string', 'mysql_database' => 'string', 'mysql_conf' => 'string', @@ -909,6 +911,7 @@ class DatabasesController extends Controller 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'], + 'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'], 'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'], 'mysql_database' => ['type' => 'string', 'description' => 'MySQL database'], 'mysql_conf' => ['type' => 'string', 'description' => 'MySQL conf'], @@ -1013,7 +1016,7 @@ class DatabasesController extends Controller public function create_database(Request $request, NewDatabaseTypes $type) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { @@ -1220,9 +1223,10 @@ class DatabasesController extends Controller return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::MYSQL) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $validator = customApiValidator($request->all(), [ 'mysql_root_password' => 'string', + 'mysql_password' => 'string', 'mysql_user' => 'string', 'mysql_database' => 'string', 'mysql_conf' => 'string', From f279729f088629ecdf4de3b058540c40c34f19bb Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sun, 1 Dec 2024 10:16:41 +1100 Subject: [PATCH 24/75] fix: incorrect MongoDB init API property --- app/Http/Controllers/Api/DatabasesController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index 21816557f..58c4467e9 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -211,7 +211,7 @@ class DatabasesController extends Controller 'mongo_conf' => ['type' => 'string', 'description' => 'Mongo conf'], 'mongo_initdb_root_username' => ['type' => 'string', 'description' => 'Mongo initdb root username'], 'mongo_initdb_root_password' => ['type' => 'string', 'description' => 'Mongo initdb root password'], - 'mongo_initdb_init_database' => ['type' => 'string', 'description' => 'Mongo initdb init database'], + 'mongo_initdb_database' => ['type' => 'string', 'description' => 'Mongo initdb init database'], 'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'], 'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'], 'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'], @@ -242,7 +242,7 @@ class DatabasesController extends Controller )] public function update_by_uuid(Request $request) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); @@ -414,12 +414,12 @@ class DatabasesController extends Controller } break; case 'standalone-mongodb': - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database']; $validator = customApiValidator($request->all(), [ 'mongo_conf' => 'string', 'mongo_initdb_root_username' => 'string', 'mongo_initdb_root_password' => 'string', - 'mongo_initdb_init_database' => 'string', + 'mongo_initdb_database' => 'string', ]); if ($request->has('mongo_conf')) { if (! isBase64Encoded($request->mongo_conf)) { @@ -1016,7 +1016,7 @@ class DatabasesController extends Controller public function create_database(Request $request, NewDatabaseTypes $type) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { @@ -1460,12 +1460,12 @@ class DatabasesController extends Controller return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::MONGODB) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database']; $validator = customApiValidator($request->all(), [ 'mongo_conf' => 'string', 'mongo_initdb_root_username' => 'string', 'mongo_initdb_root_password' => 'string', - 'mongo_initdb_init_database' => 'string', + 'mongo_initdb_database' => 'string', ]); $extraFields = array_diff(array_keys($request->all()), $allowedFields); if ($validator->fails() || ! empty($extraFields)) { From ce6602eb6398a2f99edb4180b300d89d19e186d5 Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sun, 1 Dec 2024 10:17:49 +1100 Subject: [PATCH 25/75] chore: regenerate openapi spec --- openapi.json | 10 +++++++++- openapi.yaml | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/openapi.json b/openapi.json index 2ec218438..5d35331ec 100644 --- a/openapi.json +++ b/openapi.json @@ -3011,7 +3011,7 @@ "type": "string", "description": "Mongo initdb root password" }, - "mongo_initdb_init_database": { + "mongo_initdb_database": { "type": "string", "description": "Mongo initdb init database" }, @@ -3019,6 +3019,10 @@ "type": "string", "description": "MySQL root password" }, + "mysql_password": { + "type": "string", + "description": "MySQL password" + }, "mysql_user": { "type": "string", "description": "MySQL user" @@ -3842,6 +3846,10 @@ "type": "string", "description": "MySQL root password" }, + "mysql_password": { + "type": "string", + "description": "MySQL password" + }, "mysql_user": { "type": "string", "description": "MySQL user" diff --git a/openapi.yaml b/openapi.yaml index 2a22c730c..20bf34873 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2089,12 +2089,15 @@ paths: mongo_initdb_root_password: type: string description: 'Mongo initdb root password' - mongo_initdb_init_database: + mongo_initdb_database: type: string description: 'Mongo initdb init database' mysql_root_password: type: string description: 'MySQL root password' + mysql_password: + type: string + description: 'MySQL password' mysql_user: type: string description: 'MySQL user' @@ -2684,6 +2687,9 @@ paths: mysql_root_password: type: string description: 'MySQL root password' + mysql_password: + type: string + description: 'MySQL password' mysql_user: type: string description: 'MySQL user' From f6c0a92e95eb40a86b1b7a2415449c9dcdf763dd Mon Sep 17 00:00:00 2001 From: Mahmudul Hasan Sayan Date: Sun, 1 Dec 2024 10:10:40 +0600 Subject: [PATCH 26/75] Added kvm devices information in macos.yaml --- templates/compose/macos.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/compose/macos.yaml b/templates/compose/macos.yaml index 70cebc144..018c55602 100644 --- a/templates/compose/macos.yaml +++ b/templates/compose/macos.yaml @@ -12,6 +12,8 @@ services: environment: - SERVICE_FQDN_MACOS_8006 - VERSION=15 + devices: + - /dev/kvm:/dev/kvm cap_add: - NET_ADMIN stop_grace_period: 2m From a8a5e29862ce5c124d2250b4204f21f95da02b17 Mon Sep 17 00:00:00 2001 From: Mahmudul Hasan Sayan Date: Sun, 1 Dec 2024 10:11:22 +0600 Subject: [PATCH 27/75] Added kvm devices information in windows.yaml --- templates/compose/windows.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/compose/windows.yaml b/templates/compose/windows.yaml index ddbc4c3dc..f95f60797 100644 --- a/templates/compose/windows.yaml +++ b/templates/compose/windows.yaml @@ -12,6 +12,8 @@ services: environment: - SERVICE_FQDN_WINDOWS_8006 - VERSION=11 + devices: + - /dev/kvm:/dev/kvm cap_add: - NET_ADMIN stop_grace_period: 2m From 99fcd98e639a5c79583fea0c4977c444dbbd1e8d Mon Sep 17 00:00:00 2001 From: Oliver Frost Date: Sun, 1 Dec 2024 14:51:47 +0700 Subject: [PATCH 28/75] Added slogan to be included, when searching resources --- resources/views/livewire/project/new/select.blade.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index bc31d4530..323191bfd 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -169,7 +169,8 @@ } const filtered = Object.values(items).filter(item => { return (item.name?.toLowerCase().includes(searchLower) || - item.description?.toLowerCase().includes(searchLower)) + item.description?.toLowerCase().includes(searchLower) || + item.slogan?.toLowerCase().includes(searchLower)) }) return isSort ? filtered.sort(sortFn) : filtered; }, From 062c198d1dd503843074f2bbde2c080d1da3963d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 08:08:09 +0100 Subject: [PATCH 29/75] test zoom out --- resources/css/app.css | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/resources/css/app.css b/resources/css/app.css index 1195a3058..c3d46cc04 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -4,9 +4,23 @@ @tailwind components; @tailwind utilities; +html { + font-size: 93.75%; +} + +:root { + --vh: 1vh; +} + +@screen lg { + html { + font-size: 87.5%; + } +} + html, body { - @apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400; + @apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400 w-full; } body { From 428ba67cc0171a46b298f0f130bb9f44a634229d Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:22:49 +0100 Subject: [PATCH 30/75] Disable Windows and MacOS Windows and MacOS templates need KVM to work, the problem is that KVM is only available on Linux. The second problem is that KVM needs to be installed on the host server, but most Coolify users use a VPS to host it, and almost all VPS providers block KVM access or installation on the host because the hole server is already KVM virtualized. --- templates/compose/macos.yaml | 1 + templates/compose/windows.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/templates/compose/macos.yaml b/templates/compose/macos.yaml index 018c55602..ab05cece5 100644 --- a/templates/compose/macos.yaml +++ b/templates/compose/macos.yaml @@ -1,3 +1,4 @@ +# ignore: true # documentation: https://github.com/dockur/macos # slogan: Run macOS in a containerized environment. # tags: macos, virtualization, container, os diff --git a/templates/compose/windows.yaml b/templates/compose/windows.yaml index f95f60797..f1d2a8573 100644 --- a/templates/compose/windows.yaml +++ b/templates/compose/windows.yaml @@ -1,3 +1,4 @@ +# ignore: true # documentation: https://github.com/dockur/windows # slogan: Run Windows in a containerized environment. # tags: windows, virtualization, container, os From d7703efcdc53c8b915b5cf3f42212e2e34557d75 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 13:12:25 +0100 Subject: [PATCH 31/75] optimize db query --- .../Project/Application/Configuration.php | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/app/Livewire/Project/Application/Configuration.php b/app/Livewire/Project/Application/Configuration.php index 5e7f83772..cce3bdd39 100644 --- a/app/Livewire/Project/Application/Configuration.php +++ b/app/Livewire/Project/Application/Configuration.php @@ -16,29 +16,24 @@ class Configuration extends Component public function mount() { - $project = currentTeam() - ->projects() - ->select('id', 'uuid', 'team_id') - ->where('uuid', request()->route('project_uuid')) - ->firstOrFail(); - - $environment = $project->environments() - ->select('id', 'name', 'project_id') - ->where('name', request()->route('environment_name')) - ->firstOrFail(); - - $application = $environment->applications() - ->with(['destination']) + $this->application = Application::query() + ->whereHas('environment.project', function ($query) { + $query->where('team_id', currentTeam()->id) + ->where('uuid', request()->route('project_uuid')); + }) + ->whereHas('environment', function ($query) { + $query->where('name', request()->route('environment_name')); + }) ->where('uuid', request()->route('application_uuid')) + ->with(['destination' => function ($query) { + $query->select('id', 'server_id'); + }]) ->firstOrFail(); - $this->application = $application; - - if ($application->destination && $application->destination->server) { - $mainServer = $application->destination->server; + if ($this->application->destination && $this->application->destination->server_id) { $this->servers = Server::ownedByCurrentTeam() ->select('id', 'name') - ->where('id', '!=', $mainServer->id) + ->where('id', '!=', $this->application->destination->server_id) ->get(); } else { $this->servers = collect(); From 2a9d9830a3f0c33ac0c51ecdb0edefc34fe76e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Mon, 2 Dec 2024 07:37:07 -0500 Subject: [PATCH 32/75] commented out devices section --- templates/compose/plex.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/compose/plex.yaml b/templates/compose/plex.yaml index 8d55ed5bc..0bc6e0334 100644 --- a/templates/compose/plex.yaml +++ b/templates/compose/plex.yaml @@ -14,8 +14,8 @@ services: - PGID=1000 - TZ=${TZ:-America/Toronto} - PLEX_CLAIM=${PLEX_CLAIM} - devices: - - "/dev/dri:/dev/dri" + #devices: + # - "/dev/dri:/dev/dri" volumes: - plex-config:/config - plex-tvshows:/data/tvshows From 8b634252d12afeda75af7ad91039c4149e58de94 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:13:09 +0100 Subject: [PATCH 33/75] Update service-templates.json --- templates/service-templates.json | 49 ++++++++++++-------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/templates/service-templates.json b/templates/service-templates.json index 36a2cf4d9..0fff92f86 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -233,7 +233,7 @@ "budibase": { "documentation": "https://docs.budibase.com/docs/docker-compose?utm_source=coolify.io", "slogan": "Low code platform for building business apps and workflows in minutes. Supports PostgreSQL, MySQL, MSSQL, MongoDB, Rest API, Docker, K8s, and more.", - "compose": "c2VydmljZXM6CiAgYXBwLXNlcnZpY2U6CiAgICBpbWFnZTogYnVkaWJhc2UuZG9ja2VyLnNjYXJmLnNoL2J1ZGliYXNlL2FwcHMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFTEZfSE9TVEVEPTEKICAgICAgLSAnQ09VQ0hfREJfVVJMPWh0dHA6Ly8kU0VSVklDRV9VU0VSX0JVRElCQVNFX0NPVUNIREI6JFNFUlZJQ0VfUEFTU1dPUkRfQlVESUJBU0VfQ09VQ0hEQkBjb3VjaGRiLXNlcnZpY2U6NTk4NCcKICAgICAgLSAnV09SS0VSX1VSTD1odHRwOi8vd29ya2VyLXNlcnZpY2U6NDAwMycKICAgICAgLSAnTUlOSU9fVVJMPWh0dHA6Ly9taW5pby1zZXJ2aWNlOjkwMDAnCiAgICAgIC0gTUlOSU9fQUNDRVNTX0tFWT0kU0VSVklDRV9VU0VSX0JVRElCQVNFX01JTklPCiAgICAgIC0gTUlOSU9fU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9CVURJQkFTRV9NSU5JTwogICAgICAtIElOVEVSTkFMX0FQSV9LRVk9JFNFUlZJQ0VfQkFTRTY0XzEyOF9CVURJQkFTRQogICAgICAtICdCVURJQkFTRV9FTlZJUk9OTUVOVD0ke0JVRElCQVNFX0VOVklST05NRU5UOi1QUk9EVUNUSU9OfScKICAgICAgLSBQT1JUPTQwMDIKICAgICAgLSBBUElfRU5DUllQVElPTl9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0JVRElCQVNFCiAgICAgIC0gSldUX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfQlVESUJBU0UKICAgICAgLSBMT0dfTEVWRUw9aW5mbwogICAgICAtICdFTkFCTEVfQU5BTFlUSUNTPSR7RU5BQkxFX0FOQUxZVElDUzotdHJ1ZX0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpcy1zZXJ2aWNlOjYzNzknCiAgICAgIC0gUkVESVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQlVESUJBU0VfUkVESVMKICAgICAgLSBCQl9BRE1JTl9VU0VSX0VNQUlMPQogICAgICAtIEJCX0FETUlOX1VTRVJfUEFTU1dPUkQ9CiAgICBkZXBlbmRzX29uOgogICAgICAtIHdvcmtlci1zZXJ2aWNlCiAgICAgIC0gcmVkaXMtc2VydmljZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vYXBwLXNlcnZpY2U6NDAwMicKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAxNXMKICAgICAgcmV0cmllczogNQogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIHdvcmtlci1zZXJ2aWNlOgogICAgaW1hZ2U6IGJ1ZGliYXNlLmRvY2tlci5zY2FyZi5zaC9idWRpYmFzZS93b3JrZXIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFTEZfSE9TVEVEPTEKICAgICAgLSBQT1JUPTQwMDMKICAgICAgLSBDTFVTVEVSX1BPUlQ9MTAwMDAKICAgICAgLSBBUElfRU5DUllQVElPTl9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0JVRElCQVNFCiAgICAgIC0gSldUX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfQlVESUJBU0UKICAgICAgLSBNSU5JT19BQ0NFU1NfS0VZPSRTRVJWSUNFX1VTRVJfQlVESUJBU0VfTUlOSU8KICAgICAgLSBNSU5JT19TRUNSRVRfS0VZPSRTRVJWSUNFX1BBU1NXT1JEX0JVRElCQVNFX01JTklPCiAgICAgIC0gJ01JTklPX1VSTD1odHRwOi8vbWluaW8tc2VydmljZTo5MDAwJwogICAgICAtICdBUFBTX1VSTD1odHRwOi8vYXBwLXNlcnZpY2U6NDAwMicKICAgICAgLSBDT1VDSF9EQl9VU0VSTkFNRT0kU0VSVklDRV9VU0VSX0JVRElCQVNFX0NPVUNIREIKICAgICAgLSBDT1VDSF9EQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9CVURJQkFTRV9DT1VDSERCCiAgICAgIC0gJ0NPVUNIX0RCX1VSTD1odHRwOi8vJFNFUlZJQ0VfVVNFUl9CVURJQkFTRV9DT1VDSERCOiRTRVJWSUNFX1BBU1NXT1JEX0JVRElCQVNFX0NPVUNIREJAY291Y2hkYi1zZXJ2aWNlOjU5ODQnCiAgICAgIC0gSU5URVJOQUxfQVBJX0tFWT0kU0VSVklDRV9CQVNFNjRfMTI4X0JVRElCQVNFCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpcy1zZXJ2aWNlOjYzNzknCiAgICAgIC0gUkVESVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQlVESUJBU0VfUkVESVMKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMtc2VydmljZQogICAgICAtIG1pbmlvLXNlcnZpY2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL3dvcmtlci1zZXJ2aWNlOjQwMDMnCiAgICAgIGludGVydmFsOiAxNXMKICAgICAgdGltZW91dDogMTVzCiAgICAgIHJldHJpZXM6IDUKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICBtaW5pby1zZXJ2aWNlOgogICAgaW1hZ2U6IG1pbmlvL21pbmlvCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pb19kYXRhOi9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fQUNDRVNTX0tFWT0kU0VSVklDRV9VU0VSX0JVRElCQVNFX01JTklPCiAgICAgIC0gTUlOSU9fU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9CVURJQkFTRV9NSU5JTwogICAgICAtIE1JTklPX0JST1dTRVI9b2ZmCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tY29uc29sZS1hZGRyZXNzICI6OTAwMSInCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6OTAwMC9taW5pby9oZWFsdGgvbGl2ZScKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwogIHByb3h5LXNlcnZpY2U6CiAgICBpbWFnZTogYnVkaWJhc2UvcHJveHkKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9CVURJQkFTRV8xMDAwMAogICAgICAtIFBST1hZX1JBVEVfTElNSVRfV0VCSE9PS1NfUEVSX1NFQ09ORD0xMAogICAgICAtIFBST1hZX1JBVEVfTElNSVRfQVBJX1BFUl9TRUNPTkQ9MjAKICAgICAgLSAnQVBQU19VUFNUUkVBTV9VUkw9aHR0cDovL2FwcC1zZXJ2aWNlOjQwMDInCiAgICAgIC0gJ1dPUktFUl9VUFNUUkVBTV9VUkw9aHR0cDovL3dvcmtlci1zZXJ2aWNlOjQwMDMnCiAgICAgIC0gJ01JTklPX1VQU1RSRUFNX1VSTD1odHRwOi8vbWluaW8tc2VydmljZTo5MDAwJwogICAgICAtICdDT1VDSERCX1VQU1RSRUFNX1VSTD1odHRwOi8vY291Y2hkYi1zZXJ2aWNlOjU5ODQnCiAgICAgIC0gJ1dBVENIVE9XRVJfVVBTVFJFQU1fVVJMPWh0dHA6Ly93YXRjaHRvd2VyLXNlcnZpY2U6ODA4MCcKICAgICAgLSBSRVNPTFZFUj0xMjcuMC4wLjExCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1pbmlvLXNlcnZpY2UKICAgICAgLSB3b3JrZXItc2VydmljZQogICAgICAtIGFwcC1zZXJ2aWNlCiAgICAgIC0gY291Y2hkYi1zZXJ2aWNlCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6MTAwMDAvJwogICAgICBpbnRlcnZhbDogMTVzCiAgICAgIHRpbWVvdXQ6IDE1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgY291Y2hkYi1zZXJ2aWNlOgogICAgaW1hZ2U6IGJ1ZGliYXNlL2NvdWNoZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIENPVUNIREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQlVESUJBU0VfQ09VQ0hEQgogICAgICAtIENPVUNIREJfVVNFUj0kU0VSVklDRV9VU0VSX0JVRElCQVNFX0NPVUNIREIKICAgICAgLSBUQVJHRVRCVUlMRD1kb2NrZXItY29tcG9zZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjU5ODQvJwogICAgICBpbnRlcnZhbDogMTVzCiAgICAgIHRpbWVvdXQ6IDE1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgICB2b2x1bWVzOgogICAgICAtICdjb3VjaGRiM19kYXRhOi9vcHQvY291Y2hkYi9kYXRhJwogIHJlZGlzLXNlcnZpY2U6CiAgICBpbWFnZTogcmVkaXMKICAgIGNvbW1hbmQ6ICdyZWRpcy1zZXJ2ZXIgLS1yZXF1aXJlcGFzcyAiJFNFUlZJQ0VfUEFTU1dPUkRfQlVESUJBU0VfUkVESVMiJwogICAgdm9sdW1lczoKICAgICAgLSAncmVkaXNfZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtICctYScKICAgICAgICAtICRTRVJWSUNFX1BBU1NXT1JEX0JVRElCQVNFX1JFRElTCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiAxNXMKICAgICAgdGltZW91dDogMTVzCiAgICAgIHJldHJpZXM6IDUKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICB3YXRjaHRvd2VyLXNlcnZpY2U6CiAgICByZXN0YXJ0OiBhbHdheXMKICAgIGltYWdlOiBjb250YWlucnJyL3dhdGNodG93ZXIKICAgIHZvbHVtZXM6CiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrJwogICAgY29tbWFuZDogJy0tZGVidWcgLS1odHRwLWFwaS11cGRhdGUgYmJhcHBzIGJid29ya2VyIGJicHJveHknCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBXQVRDSFRPV0VSX0hUVFBfQVBJPXRydWUKICAgICAgLSBXQVRDSFRPV0VSX0hUVFBfQVBJX1RPS0VOPSRTRVJWSUNFX1BBU1NXT1JEX0JVRElCQVNFX1dBVENIVE9XRVIKICAgICAgLSBXQVRDSFRPV0VSX0NMRUFOVVA9dHJ1ZQogICAgbGFiZWxzOgogICAgICAtIGNvbS5jZW50dXJ5bGlua2xhYnMud2F0Y2h0b3dlci5lbmFibGU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL3dhdGNodG93ZXItc2VydmljZTo4MDgwJwogICAgICBpbnRlcnZhbDogMTVzCiAgICAgIHRpbWVvdXQ6IDE1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCg==", + "compose": "c2VydmljZXM6CiAgYXBwLXNlcnZpY2U6CiAgICBpbWFnZTogYnVkaWJhc2UuZG9ja2VyLnNjYXJmLnNoL2J1ZGliYXNlL2FwcHMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFTEZfSE9TVEVEPTEKICAgICAgLSAnQ09VQ0hfREJfVVJMPWh0dHA6Ly8kU0VSVklDRV9VU0VSX0NPVUNIREI6JFNFUlZJQ0VfUEFTU1dPUkRfQ09VQ0hEQkBjb3VjaGRiLXNlcnZpY2U6NTk4NCcKICAgICAgLSAnV09SS0VSX1VSTD1odHRwOi8vd29ya2VyLXNlcnZpY2U6NDAwMycKICAgICAgLSAnTUlOSU9fVVJMPWh0dHA6Ly9taW5pby1zZXJ2aWNlOjkwMDAnCiAgICAgIC0gTUlOSU9fQUNDRVNTX0tFWT0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgICAtIElOVEVSTkFMX0FQSV9LRVk9JFNFUlZJQ0VfQkFTRTY0XzEyOF9CVURJQkFTRQogICAgICAtICdCVURJQkFTRV9FTlZJUk9OTUVOVD0ke0JVRElCQVNFX0VOVklST05NRU5UOi1QUk9EVUNUSU9OfScKICAgICAgLSBQT1JUPTQwMDIKICAgICAgLSBBUElfRU5DUllQVElPTl9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0JVRElCQVNFCiAgICAgIC0gSldUX1NFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfQlVESUJBU0UKICAgICAgLSBMT0dfTEVWRUw9aW5mbwogICAgICAtICdFTkFCTEVfQU5BTFlUSUNTPSR7RU5BQkxFX0FOQUxZVElDUzotdHJ1ZX0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpcy1zZXJ2aWNlOjYzNzknCiAgICAgIC0gUkVESVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUkVESVMKICAgICAgLSBCQl9BRE1JTl9VU0VSX0VNQUlMPQogICAgICAtIEJCX0FETUlOX1VTRVJfUEFTU1dPUkQ9CiAgICBkZXBlbmRzX29uOgogICAgICAtIHdvcmtlci1zZXJ2aWNlCiAgICAgIC0gcmVkaXMtc2VydmljZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICctcU8tJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NDAwMi9oZWFsdGgnCiAgICAgIGludGVydmFsOiAxNXMKICAgICAgdGltZW91dDogMTVzCiAgICAgIHJldHJpZXM6IDUKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICB3b3JrZXItc2VydmljZToKICAgIGltYWdlOiBidWRpYmFzZS5kb2NrZXIuc2NhcmYuc2gvYnVkaWJhc2Uvd29ya2VyCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRUxGX0hPU1RFRD0xCiAgICAgIC0gUE9SVD00MDAzCiAgICAgIC0gQ0xVU1RFUl9QT1JUPTEwMDAwCiAgICAgIC0gQVBJX0VOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9CVURJQkFTRQogICAgICAtIEpXVF9TRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzY0X0JVRElCQVNFCiAgICAgIC0gTUlOSU9fQUNDRVNTX0tFWT0kU0VSVklDRV9VU0VSX01JTklPCiAgICAgIC0gTUlOSU9fU0VDUkVUX0tFWT0kU0VSVklDRV9QQVNTV09SRF9NSU5JTwogICAgICAtICdNSU5JT19VUkw9aHR0cDovL21pbmlvLXNlcnZpY2U6OTAwMCcKICAgICAgLSAnQVBQU19VUkw9aHR0cDovL2FwcC1zZXJ2aWNlOjQwMDInCiAgICAgIC0gQ09VQ0hfREJfVVNFUk5BTUU9JFNFUlZJQ0VfVVNFUl9DT1VDSERCCiAgICAgIC0gQ09VQ0hfREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQ09VQ0hEQgogICAgICAtICdDT1VDSF9EQl9VUkw9aHR0cDovLyRTRVJWSUNFX1VTRVJfQ09VQ0hEQjokU0VSVklDRV9QQVNTV09SRF9DT1VDSERCQGNvdWNoZGItc2VydmljZTo1OTg0JwogICAgICAtIElOVEVSTkFMX0FQSV9LRVk9JFNFUlZJQ0VfQkFTRTY0XzEyOF9CVURJQkFTRQogICAgICAtICdSRURJU19VUkw9cmVkaXMtc2VydmljZTo2Mzc5JwogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICBkZXBlbmRzX29uOgogICAgICAtIHJlZGlzLXNlcnZpY2UKICAgICAgLSBtaW5pby1zZXJ2aWNlCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJy1xTy0nCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo0MDAzL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAxNXMKICAgICAgcmV0cmllczogNQogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIG1pbmlvLXNlcnZpY2U6CiAgICBpbWFnZTogbWluaW8vbWluaW8KICAgIHZvbHVtZXM6CiAgICAgIC0gJ21pbmlvX2RhdGE6L2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JFNFUlZJQ0VfVVNFUl9NSU5JTwogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUlOSU8KICAgICAgLSBNSU5JT19CUk9XU0VSPW9mZgogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjkwMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDMKICBwcm94eS1zZXJ2aWNlOgogICAgaW1hZ2U6IGJ1ZGliYXNlL3Byb3h5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQlVESUJBU0VfMTAwMDAKICAgICAgLSBQUk9YWV9SQVRFX0xJTUlUX1dFQkhPT0tTX1BFUl9TRUNPTkQ9MTAKICAgICAgLSBQUk9YWV9SQVRFX0xJTUlUX0FQSV9QRVJfU0VDT05EPTIwCiAgICAgIC0gJ0FQUFNfVVBTVFJFQU1fVVJMPWh0dHA6Ly9hcHAtc2VydmljZTo0MDAyJwogICAgICAtICdXT1JLRVJfVVBTVFJFQU1fVVJMPWh0dHA6Ly93b3JrZXItc2VydmljZTo0MDAzJwogICAgICAtICdNSU5JT19VUFNUUkVBTV9VUkw9aHR0cDovL21pbmlvLXNlcnZpY2U6OTAwMCcKICAgICAgLSAnQ09VQ0hEQl9VUFNUUkVBTV9VUkw9aHR0cDovL2NvdWNoZGItc2VydmljZTo1OTg0JwogICAgICAtICdXQVRDSFRPV0VSX1VQU1RSRUFNX1VSTD1odHRwOi8vd2F0Y2h0b3dlci1zZXJ2aWNlOjgwODAnCiAgICAgIC0gUkVTT0xWRVI9MTI3LjAuMC4xMQogICAgZGVwZW5kc19vbjoKICAgICAgLSBtaW5pby1zZXJ2aWNlCiAgICAgIC0gd29ya2VyLXNlcnZpY2UKICAgICAgLSBhcHAtc2VydmljZQogICAgICAtIGNvdWNoZGItc2VydmljZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjEwMDAwLycKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAxNXMKICAgICAgcmV0cmllczogNQogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIGNvdWNoZGItc2VydmljZToKICAgIGltYWdlOiBidWRpYmFzZS9jb3VjaGRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBDT1VDSERCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX0NPVUNIREIKICAgICAgLSBDT1VDSERCX1VTRVI9JFNFUlZJQ0VfVVNFUl9DT1VDSERCCiAgICAgIC0gVEFSR0VUQlVJTEQ9ZG9ja2VyLWNvbXBvc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo1OTg0LycKICAgICAgaW50ZXJ2YWw6IDE1cwogICAgICB0aW1lb3V0OiAxNXMKICAgICAgcmV0cmllczogNQogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogICAgdm9sdW1lczoKICAgICAgLSAnY291Y2hkYjNfZGF0YTovb3B0L2NvdWNoZGIvZGF0YScKICByZWRpcy1zZXJ2aWNlOgogICAgaW1hZ2U6IHJlZGlzCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tcmVxdWlyZXBhc3MgIiRTRVJWSUNFX1BBU1NXT1JEX1JFRElTIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3JlZGlzX2RhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSAnLWEnCiAgICAgICAgLSAkU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogMTVzCiAgICAgIHRpbWVvdXQ6IDE1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgd2F0Y2h0b3dlci1zZXJ2aWNlOgogICAgcmVzdGFydDogYWx3YXlzCiAgICBpbWFnZTogY29udGFpbnJyci93YXRjaHRvd2VyCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgIGNvbW1hbmQ6ICctLWRlYnVnIC0taHR0cC1hcGktdXBkYXRlIGJiYXBwcyBiYndvcmtlciBiYnByb3h5JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gV0FUQ0hUT1dFUl9IVFRQX0FQST10cnVlCiAgICAgIC0gV0FUQ0hUT1dFUl9IVFRQX0FQSV9UT0tFTj0kU0VSVklDRV9QQVNTV09SRF9XQVRDSFRPV0VSCiAgICAgIC0gV0FUQ0hUT1dFUl9DTEVBTlVQPXRydWUKICAgIGxhYmVsczoKICAgICAgLSBjb20uY2VudHVyeWxpbmtsYWJzLndhdGNodG93ZXIuZW5hYmxlPWZhbHNlCiAgICBleGNsdWRlX2Zyb21faGM6IHRydWUK", "tags": [ "budibase", "low-code", @@ -851,7 +851,7 @@ "formbricks": { "documentation": "https://formbricks.com/docs/self-hosting/configuration?utm_source=coolify.io", "slogan": "Open Source Survey Platform", - "compose": "c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZ2hjci5pby9mb3JtYnJpY2tzL2Zvcm1icmlja3M6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZPUk1CUklDS1NfMzAwMAogICAgICAtIFdFQkFQUF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzcWw6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gTkVYVEFVVEhfU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF82NF9ORVhUQVVUSAogICAgICAtIE5FWFRBVVRIX1VSTD0kU0VSVklDRV9GUUROX0ZPUk1CUklDS1MKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtIENST05fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF82NF9DUk9OCiAgICAgIC0gJ0VOVEVSUFJJU0VfTElDRU5TRV9LRVk9JHtFTlRFUlBSSVNFX0xJQ0VOU0VfS0VZfScKICAgICAgLSAnTUFJTF9GUk9NPSR7TUFJTF9GUk9NOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9IT1NUPSR7U01UUF9IT1NUOi10ZXN0LmV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9QT1JUPSR7U01UUF9QT1JUOi01ODd9JwogICAgICAtICdTTVRQX1VTRVI9JHtTTVRQX1VTRVI6LXRlc3R9JwogICAgICAtICdTTVRQX1BBU1NXT1JEPSR7U01UUF9QQVNTV09SRDotdGVzdH0nCiAgICAgIC0gJ1NNVFBfU0VDVVJFX0VOQUJMRUQ9JHtTTVRQX1NFQ1VSRV9FTkFCTEVEOi0wfScKICAgICAgLSAnU01UUF9SRUpFQ1RfVU5BVVRIT1JJWkVEX1RMUz0ke1NNVFBfUkVKRUNUX1VOQVVUSE9SSVpFRF9UTFM6LTF9JwogICAgICAtICdTSE9SVF9VUkxfQkFTRT0ke1NIT1JUX1VSTF9CQVNFfScKICAgICAgLSAnRU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEPSR7RU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEOi0xfScKICAgICAgLSAnUEFTU1dPUkRfUkVTRVRfRElTQUJMRUQ9JHtQQVNTV09SRF9SRVNFVF9ESVNBQkxFRDotMX0nCiAgICAgIC0gJ0VNQUlMX0FVVEhfRElTQUJMRUQ9JHtFTUFJTF9BVVRIX0RJU0FCTEVEOi0wfScKICAgICAgLSAnSU5WSVRFX0RJU0FCTEVEPSR7SU5WSVRFX0RJU0FCTEVEOi0wfScKICAgICAgLSAnQVNTRVRfUFJFRklYX1VSTD0ke0FTU0VUX1BSRUZJWF9VUkx9JwogICAgICAtICdVTlNQTEFTSF9BQ0NFU1NfS0VZPSR7VU5TUExBU0hfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0dJVEhVQl9JRD0ke0dJVEhVQl9JRH0nCiAgICAgIC0gJ0dJVEhVQl9TRUNSRVQ9JHtHSVRIVUJfU0VDUkVUfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVBRF9DTElFTlRfSUQ9JHtBWlVSRUFEX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FaVVJFQURfQ0xJRU5UX1NFQ1JFVD0ke0FaVVJFQURfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0FaVVJFQURfVEVOQU5UX0lEPSR7QVpVUkVBRF9URU5BTlRfSUR9JwogICAgICAtICdPSURDX0NMSUVOVF9JRD0ke09JRENfQ0xJRU5UX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfU0VDUkVUPSR7T0lEQ19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnT0lEQ19JU1NVRVI9JHtPSURDX0lTU1VFUn0nCiAgICAgIC0gJ09JRENfRElTUExBWV9OQU1FPSR7T0lEQ19ESVNQTEFZX05BTUV9JwogICAgICAtICdPSURDX1NJR05JTkdfQUxHT1JJVEhNPSR7T0lEQ19TSUdOSU5HX0FMR09SSVRITX0nCiAgICAgIC0gJ05PVElPTl9PQVVUSF9DTElFTlRfSUQ9JHtOT1RJT05fT0FVVEhfQ0xJRU5UX0lEfScKICAgICAgLSAnTk9USU9OX09BVVRIX0NMSUVOVF9TRUNSRVQ9JHtOT1RJT05fT0FVVEhfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9TSEVFVFNfQ0xJRU5UX0lEPSR7R09PR0xFX1NIRUVUU19DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfU0hFRVRTX0NMSUVOVF9TRUNSRVQ9JHtHT09HTEVfU0hFRVRTX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdHT09HTEVfU0hFRVRTX1JFRElSRUNUX1VSTD0ke0dPT0dMRV9TSEVFVFNfUkVESVJFQ1RfVVJMfScKICAgICAgLSAnQUlSVEFCTEVfQ0xJRU5UX0lEPSR7QUlSVEFCTEVfQ0xJRU5UX0lEfScKICAgICAgLSAnU0xBQ0tfQ0xJRU5UX0lEPSR7U0xBQ0tfQ0xJRU5UX0lEfScKICAgICAgLSAnU0xBQ0tfQ0xJRU5UX1NFQ1JFVD0ke1NMQUNLX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdQUklWQUNZX1VSTD0ke1BSSVZBQ1lfVVJMfScKICAgICAgLSAnVEVSTVNfVVJMPSR7VEVSTVNfVVJMfScKICAgICAgLSAnSU1QUklOVF9VUkw9JHtJTVBSSU5UX1VSTH0nCiAgICAgIC0gJ1JBVEVfTElNSVRJTkdfRElTQUJMRUQ9JHtSQVRFX0xJTUlUSU5HX0RJU0FCTEVEOi0wfScKICAgICAgLSAnT1BFTlRFTEVNRVRSWV9MSVNURU5FUl9VUkw9JHtPUEVOVEVMRU1FVFJZX0xJU1RFTkVSX1VSTH0nCiAgICAgIC0gJ1JFRElTX1VSTD0ke1JFRElTX1VSTH0nCiAgICAgIC0gJ1JFRElTX0hUVFBfVVJMPSR7UkVESVNfSFRUUF9VUkx9JwogICAgICAtICdERUZBVUxUX09SR0FOSVpBVElPTl9JRD0ke0RFRkFVTFRfT1JHQU5JWkFUSU9OX0lEfScKICAgICAgLSAnREVGQVVMVF9PUkdBTklaQVRJT05fUk9MRT0ke0RFRkFVTFRfT1JHQU5JWkFUSU9OX1JPTEU6LWFkbWlufScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Zvcm1icmlja3MtdXBsb2FkczovYXBwcy93ZWIvdXBsb2Fkcy8nCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Zvcm1icmlja3MtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZm9ybWJyaWNrc30nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", + "compose": "c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZ2hjci5pby9mb3JtYnJpY2tzL2Zvcm1icmlja3M6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZPUk1CUklDS1NfMzAwMAogICAgICAtIFdFQkFQUF9VUkw9JFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzcWw6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gTkVYVEFVVEhfU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF9ORVhUQVVUSAogICAgICAtIE5FWFRBVVRIX1VSTD0kU0VSVklDRV9GUUROX0ZPUk1CUklDS1MKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9CQVNFNjRfRU5DUllQVElPTgogICAgICAtIENST05fU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF9DUk9OCiAgICAgIC0gJ0VOVEVSUFJJU0VfTElDRU5TRV9LRVk9JHtFTlRFUlBSSVNFX0xJQ0VOU0VfS0VZfScKICAgICAgLSAnTUFJTF9GUk9NPSR7TUFJTF9GUk9NOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9IT1NUPSR7U01UUF9IT1NUOi10ZXN0LmV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9QT1JUPSR7U01UUF9QT1JUOi01ODd9JwogICAgICAtICdTTVRQX1VTRVI9JHtTTVRQX1VTRVI6LXRlc3R9JwogICAgICAtICdTTVRQX1BBU1NXT1JEPSR7U01UUF9QQVNTV09SRDotdGVzdH0nCiAgICAgIC0gJ1NNVFBfU0VDVVJFX0VOQUJMRUQ9JHtTTVRQX1NFQ1VSRV9FTkFCTEVEOi0wfScKICAgICAgLSAnU01UUF9SRUpFQ1RfVU5BVVRIT1JJWkVEX1RMUz0ke1NNVFBfUkVKRUNUX1VOQVVUSE9SSVpFRF9UTFM6LTF9JwogICAgICAtICdTSE9SVF9VUkxfQkFTRT0ke1NIT1JUX1VSTF9CQVNFfScKICAgICAgLSAnRU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEPSR7RU1BSUxfVkVSSUZJQ0FUSU9OX0RJU0FCTEVEOi0xfScKICAgICAgLSAnUEFTU1dPUkRfUkVTRVRfRElTQUJMRUQ9JHtQQVNTV09SRF9SRVNFVF9ESVNBQkxFRDotMX0nCiAgICAgIC0gJ0VNQUlMX0FVVEhfRElTQUJMRUQ9JHtFTUFJTF9BVVRIX0RJU0FCTEVEOi0wfScKICAgICAgLSAnSU5WSVRFX0RJU0FCTEVEPSR7SU5WSVRFX0RJU0FCTEVEOi0wfScKICAgICAgLSAnQVNTRVRfUFJFRklYX1VSTD0ke0FTU0VUX1BSRUZJWF9VUkx9JwogICAgICAtICdVTlNQTEFTSF9BQ0NFU1NfS0VZPSR7VU5TUExBU0hfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0dJVEhVQl9JRD0ke0dJVEhVQl9JRH0nCiAgICAgIC0gJ0dJVEhVQl9TRUNSRVQ9JHtHSVRIVUJfU0VDUkVUfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9JRD0ke0dPT0dMRV9DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX1NFQ1JFVD0ke0dPT0dMRV9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnQVpVUkVBRF9DTElFTlRfSUQ9JHtBWlVSRUFEX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FaVVJFQURfQ0xJRU5UX1NFQ1JFVD0ke0FaVVJFQURfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0FaVVJFQURfVEVOQU5UX0lEPSR7QVpVUkVBRF9URU5BTlRfSUR9JwogICAgICAtICdPSURDX0NMSUVOVF9JRD0ke09JRENfQ0xJRU5UX0lEfScKICAgICAgLSAnT0lEQ19DTElFTlRfU0VDUkVUPSR7T0lEQ19DTElFTlRfU0VDUkVUfScKICAgICAgLSAnT0lEQ19JU1NVRVI9JHtPSURDX0lTU1VFUn0nCiAgICAgIC0gJ09JRENfRElTUExBWV9OQU1FPSR7T0lEQ19ESVNQTEFZX05BTUV9JwogICAgICAtICdPSURDX1NJR05JTkdfQUxHT1JJVEhNPSR7T0lEQ19TSUdOSU5HX0FMR09SSVRITX0nCiAgICAgIC0gJ05PVElPTl9PQVVUSF9DTElFTlRfSUQ9JHtOT1RJT05fT0FVVEhfQ0xJRU5UX0lEfScKICAgICAgLSAnTk9USU9OX09BVVRIX0NMSUVOVF9TRUNSRVQ9JHtOT1RJT05fT0FVVEhfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0dPT0dMRV9TSEVFVFNfQ0xJRU5UX0lEPSR7R09PR0xFX1NIRUVUU19DTElFTlRfSUR9JwogICAgICAtICdHT09HTEVfU0hFRVRTX0NMSUVOVF9TRUNSRVQ9JHtHT09HTEVfU0hFRVRTX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdHT09HTEVfU0hFRVRTX1JFRElSRUNUX1VSTD0ke0dPT0dMRV9TSEVFVFNfUkVESVJFQ1RfVVJMfScKICAgICAgLSAnQUlSVEFCTEVfQ0xJRU5UX0lEPSR7QUlSVEFCTEVfQ0xJRU5UX0lEfScKICAgICAgLSAnU0xBQ0tfQ0xJRU5UX0lEPSR7U0xBQ0tfQ0xJRU5UX0lEfScKICAgICAgLSAnU0xBQ0tfQ0xJRU5UX1NFQ1JFVD0ke1NMQUNLX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdQUklWQUNZX1VSTD0ke1BSSVZBQ1lfVVJMfScKICAgICAgLSAnVEVSTVNfVVJMPSR7VEVSTVNfVVJMfScKICAgICAgLSAnSU1QUklOVF9VUkw9JHtJTVBSSU5UX1VSTH0nCiAgICAgIC0gJ1JBVEVfTElNSVRJTkdfRElTQUJMRUQ9JHtSQVRFX0xJTUlUSU5HX0RJU0FCTEVEOi0wfScKICAgICAgLSAnT1BFTlRFTEVNRVRSWV9MSVNURU5FUl9VUkw9JHtPUEVOVEVMRU1FVFJZX0xJU1RFTkVSX1VSTH0nCiAgICAgIC0gJ1JFRElTX1VSTD0ke1JFRElTX1VSTH0nCiAgICAgIC0gJ1JFRElTX0hUVFBfVVJMPSR7UkVESVNfSFRUUF9VUkx9JwogICAgICAtICdERUZBVUxUX09SR0FOSVpBVElPTl9JRD0ke0RFRkFVTFRfT1JHQU5JWkFUSU9OX0lEfScKICAgICAgLSAnREVGQVVMVF9PUkdBTklaQVRJT05fUk9MRT0ke0RFRkFVTFRfT1JHQU5JWkFUSU9OX1JPTEU6LW93bmVyfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Zvcm1icmlja3MtdXBsb2FkczovYXBwcy93ZWIvdXBsb2Fkcy8nCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3BndmVjdG9yL3BndmVjdG9yOnBnMTYnCiAgICB2b2x1bWVzOgogICAgICAtICdmb3JtYnJpY2tzLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=", "tags": [ "form", "builder", @@ -1580,20 +1580,6 @@ "logo": "svgs/logto_dark.svg", "minversion": "0.0.0" }, - "macos": { - "documentation": "https://github.com/dockur/macos?utm_source=coolify.io", - "slogan": "Run macOS in a containerized environment.", - "compose": "c2VydmljZXM6CiAgbWFjb3M6CiAgICBpbWFnZTogZG9ja3Vyci9tYWNvcwogICAgdm9sdW1lczoKICAgICAgLSAnbWFjb3Mtc3RvcmFnZTovc3RvcmFnZScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NQUNPU184MDA2CiAgICAgIC0gVkVSU0lPTj0xNQogICAgY2FwX2FkZDoKICAgICAgLSBORVRfQURNSU4KICAgIHN0b3BfZ3JhY2VfcGVyaW9kOiAybQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG5jCiAgICAgICAgLSAnLXonCiAgICAgICAgLSAxMjcuMC4wLjEKICAgICAgICAtICc4MDA2JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDEwCg==", - "tags": [ - "macos", - "virtualization", - "container", - "os" - ], - "logo": "svgs/macos.svg", - "minversion": "0.0.0", - "port": "8006" - }, "mailpit": { "documentation": "https://mailpit.axllent.org/docs/?utm_source=coolify.io", "slogan": "Email & SMTP testing tool with API for developers", @@ -2101,6 +2087,21 @@ "minversion": "0.0.0", "port": "80" }, + "overseerr": { + "documentation": "https://docs.overseerr.dev/getting-started/installation#docker?utm_source=coolify.io", + "slogan": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem.", + "compose": "c2VydmljZXM6CiAgb3ZlcnNlZXJyOgogICAgaW1hZ2U6ICdzY3R4L292ZXJzZWVycjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fT1ZFUlNFRVJSXzUwNTUKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSAnVFo9JHtUWjotQW1lcmljYS9Ub3JvbnRvfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ292ZXJzZWVyci1jb25maWc6L2FwcC9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tdHJpZXMnCiAgICAgICAgLSAnMScKICAgICAgICAtICctLW5vLXZlcmJvc2UnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo1MDU1L2FwaS92MS9zdGF0dXMnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", + "tags": [ + "media", + "request", + "plex", + "sonarr", + "radarr" + ], + "logo": "svgs/overseerr.svg", + "minversion": "0.0.0", + "port": "5055" + }, "owncloud": { "documentation": "https://owncloud.com/docs?utm_source=coolify.io", "slogan": "OwnCloud with Open Web UI integrates file management with a powerful, user-friendly interface.", @@ -2244,7 +2245,7 @@ "postiz": { "documentation": "https://docs.postiz.com?utm_source=coolify.io", "slogan": "Open source social media scheduling tool.", - "compose": "c2VydmljZXM6CiAgcG9zdGl6OgogICAgaW1hZ2U6ICdnaGNyLmlvL2dpdHJvb21ocS9wb3N0aXotYXBwOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QT1NUSVpfNTAwMAogICAgICAtICdNQUlOX1VSTD0ke1NFUlZJQ0VfRlFETl9QT1NUSVp9JwogICAgICAtICdGUk9OVEVORF9VUkw9JHtTRVJWSUNFX0ZRRE5fUE9TVElafScKICAgICAgLSAnTkVYVF9QVUJMSUNfQkFDS0VORF9VUkw9JHtTRVJWSUNFX0ZRRE5fUE9TVElafS9hcGknCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVFNFQ1JFVH0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3Jlc3FsOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH06JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1wb3N0aXotZGJ9JwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly8ke1NFUlZJQ0VfVVNFUl9SRURJU306JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfUByZWRpczo2Mzc5JwogICAgICAtICdCQUNLRU5EX0lOVEVSTkFMX1VSTD1odHRwOi8vbG9jYWxob3N0OjMwMDAnCiAgICAgIC0gJ0NMT1VERkxBUkVfQUNDT1VOVF9JRD0ke0NMT1VERkxBUkVfQUNDT1VOVF9JRH0nCiAgICAgIC0gJ0NMT1VERkxBUkVfQUNDRVNTX0tFWT0ke0NMT1VERkxBUkVfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0NMT1VERkxBUkVfU0VDUkVUX0FDQ0VTU19LRVk9JHtDTE9VREZMQVJFX1NFQ1JFVF9BQ0NFU1NfS0VZfScKICAgICAgLSAnQ0xPVURGTEFSRV9CVUNLRVROQU1FPSR7Q0xPVURGTEFSRV9CVUNLRVROQU1FfScKICAgICAgLSAnQ0xPVURGTEFSRV9CVUNLRVRfVVJMPSR7Q0xPVURGTEFSRV9CVUNLRVRfVVJMfScKICAgICAgLSAnQ0xPVURGTEFSRV9SRUdJT049JHtDTE9VREZMQVJFX1JFR0lPTn0nCiAgICAgIC0gJ1NUT1JBR0VfUFJPVklERVI9JHtTVE9SQUdFX1BST1ZJREVSOi1sb2NhbH0nCiAgICAgIC0gJ1VQTE9BRF9ESVJFQ1RPUlk9JHtVUExPQURfRElSRUNUT1JZOi0vdXBsb2Fkc30nCiAgICAgIC0gJ05FWFRfUFVCTElDX1VQTE9BRF9ESVJFQ1RPUlk9JHtORVhUX1BVQkxJQ19VUExPQURfRElSRUNUT1JZOi0vdXBsb2Fkc30nCiAgICAgIC0gJ05FWFRfUFVCTElDX1VQTE9BRF9TVEFUSUNfRElSRUNUT1JZPSR7TkVYVF9QVUJMSUNfVVBMT0FEX1NUQVRJQ19ESVJFQ1RPUll9JwogICAgICAtICdSRVNFTkRfQVBJX0tFWT0ke1JFU0VORF9BUElfS0VZfScKICAgICAgLSAnRU1BSUxfRlJPTV9BRERSRVNTPSR7RU1BSUxfRlJPTV9BRERSRVNTfScKICAgICAgLSAnRU1BSUxfRlJPTV9OQU1FPSR7RU1BSUxfRlJPTV9OQU1FfScKICAgICAgLSAnWF9BUElfS0VZPSR7U0VSVklDRV9YX0FQSX0nCiAgICAgIC0gJ1hfQVBJX1NFQ1JFVD0ke1NFUlZJQ0VfWF9TRUNSRVR9JwogICAgICAtICdMSU5LRURJTl9DTElFTlRfSUQ9JHtTRVJWSUNFX0xJTktFRElOX0lEfScKICAgICAgLSAnTElOS0VESU5fQ0xJRU5UX1NFQ1JFVD0ke1NFUlZJQ0VfTElOS0VESU5fU0VDUkVUfScKICAgICAgLSAnUkVERElUX0NMSUVOVF9JRD0ke1NFUlZJQ0VfUkVERElUX0FQSX0nCiAgICAgIC0gJ1JFRERJVF9DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9SRURESVRfU0VDUkVUfScKICAgICAgLSAnR0lUSFVCX0NMSUVOVF9JRD0ke1NFUlZJQ0VfR0lUSFVCX0lEfScKICAgICAgLSAnR0lUSFVCX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX0dJVEhVQl9TRUNSRVR9JwogICAgICAtICdUSFJFQURTX0FQUF9JRD0ke1NFUlZJQ0VfVEhSRUFEU19JRH0nCiAgICAgIC0gJ1RIUkVBRFNfQVBQX1NFQ1JFVD0ke1NFUlZJQ0VfVEhSRUFEU19TRUNSRVR9JwogICAgICAtICdGQUNFQk9PS19BUFBfSUQ9JHtTRVJWSUNFX0ZBQ0VCT09LX0lEfScKICAgICAgLSAnRkFDRUJPT0tfQVBQX1NFQ1JFVD0ke1NFUlZJQ0VfRkFDRUJPT0tfU0VDUkVUfScKICAgICAgLSAnWU9VVFVCRV9DTElFTlRfSUQ9JHtTRVJWSUNFX1lPVVRVQkVfSUR9JwogICAgICAtICdZT1VUVUJFX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX1lPVVRVQkVfU0VDUkVUfScKICAgICAgLSAnVElLVE9LX0NMSUVOVF9JRD0ke1NFUlZJQ0VfVElLVE9LX0lEfScKICAgICAgLSAnVElLVE9LX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX1RJS1RPS19TRUNSRVR9JwogICAgICAtICdQSU5URVJFU1RfQ0xJRU5UX0lEPSR7U0VSVklDRV9QSU5URVJFU1RfSUR9JwogICAgICAtICdQSU5URVJFU1RfQ0xJRU5UX1NFQ1JFVD0ke1NFUlZJQ0VfUElOVEVSRVNUX1NFQ1JFVH0nCiAgICAgIC0gJ0RSSUJCQkxFX0NMSUVOVF9JRD0ke1NFUlZJQ0VfRFJJQkJMRV9JRH0nCiAgICAgIC0gJ0RSSUJCQkxFX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX0RSSUJCTEVfU0VDUkVUfScKICAgICAgLSAnRElTQ09SRF9DTElFTlRfSUQ9JHtTRVJWSUNFX0RJU0NPUkRfSUR9JwogICAgICAtICdESVNDT1JEX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX0RJU0NPUkRfU0VDUkVUfScKICAgICAgLSAnRElTQ09SRF9CT1RfVE9LRU5fSUQ9JHtTRVJWSUNFX0RJU0NPUkRfVE9LRU59JwogICAgICAtICdTTEFDS19JRD0ke1NFUlZJQ0VfU0xBQ0tfSUR9JwogICAgICAtICdTTEFDS19TRUNSRVQ9JHtTRVJWSUNFX1NMQUNLX1NFQ1JFVH0nCiAgICAgIC0gJ1NMQUNLX1NJR05JTkdfU0VDUkVUPSR7U0xBQ0tfU0lHTklOR19TRUNSRVR9JwogICAgICAtICdNQVNUT0RPTl9DTElFTlRfSUQ9JHtTRVJWSUNFX01BU1RPRE9OX0lEfScKICAgICAgLSAnTUFTVE9ET05fQ0xJRU5UX1NFQ1JFVD0ke1NFUlZJQ0VfTUFTVE9ET05fU0VDUkVUfScKICAgICAgLSAnQkVFSElJVkVfQVBJX0tFWT0ke1NFUlZJQ0VfQkVFSElJVkVfS0VZfScKICAgICAgLSAnQkVFSElJVkVfUFVCTElDQVRJT05fSUQ9JHtTRVJWSUNFX0JFRUhJSVZFX1BVQklEfScKICAgICAgLSAnT1BFTkFJX0FQSV9LRVk9JHtTRVJWSUNFX09QRU5BSV9LRVl9JwogICAgICAtICdORVhUX1BVQkxJQ19ESVNDT1JEX1NVUFBPUlQ9JHtORVhUX1BVQkxJQ19ESVNDT1JEX1NVUFBPUlR9JwogICAgICAtICdORVhUX1BVQkxJQ19QT0xPVE5PPSR7TkVYVF9QVUJMSUNfUE9MT1ROT30nCiAgICAgIC0gJ0lTX0dFTkVSQUw9JHtJU19HRU5FUkFMOi10cnVlfScKICAgICAgLSAnTlhfQUREX1BMVUdJTlM9JHtOWF9BRERfUExVR0lOUzotZmFsc2V9JwogICAgICAtICdGRUVfQU1PVU5UPSR7RkVFX0FNT1VOVDotMC4wNX0nCiAgICAgIC0gJ1NUUklQRV9QVUJMSVNIQUJMRV9LRVk9JHtTVFJJUEVfUFVCTElTSEFCTEVfS0VZfScKICAgICAgLSAnU1RSSVBFX1NFQ1JFVF9LRVk9JHtTVFJJUEVfU0VDUkVUX0tFWX0nCiAgICAgIC0gJ1NUUklQRV9TSUdOSU5HX0tFWT0ke1NUUklQRV9TSUdOSU5HX0tFWX0nCiAgICAgIC0gJ1NUUklQRV9TSUdOSU5HX0tFWV9DT05ORUNUPSR7U1RSSVBFX1NJR05JTkdfS0VZX0NPTk5FQ1R9JwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGl6X2NvbmZpZzovY29uZmlnLycKICAgICAgLSAncG9zdGl6X3VwbG9hZHM6L3VwbG9hZHMvJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAnd2dldCAtcU8tIGh0dHA6Ly8xMjcuMC4wLjE6NTAwMC8nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTQuNScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3Rpel9wb3N0Z3Jlc3FsX2RhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1wb3N0aXotZGJ9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9IC1kICR7UE9TVEdSRVNRTF9EQVRBQkFTRTotcG9zdGl6LWRifScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgICAgLSAnUkVESVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9SRURJU30nCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tcmVxdWlyZXBhc3MgJHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3Rpel9yZWRpc19kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gJy1hJwogICAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMjAK", + "compose": "c2VydmljZXM6CiAgcG9zdGl6OgogICAgaW1hZ2U6ICdnaGNyLmlvL2dpdHJvb21ocS9wb3N0aXotYXBwOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QT1NUSVpfNTAwMAogICAgICAtICdNQUlOX1VSTD0ke1NFUlZJQ0VfRlFETl9QT1NUSVp9JwogICAgICAtICdGUk9OVEVORF9VUkw9JHtTRVJWSUNFX0ZRRE5fUE9TVElafScKICAgICAgLSAnTkVYVF9QVUJMSUNfQkFDS0VORF9VUkw9JHtTRVJWSUNFX0ZRRE5fUE9TVElafS9hcGknCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVFNFQ1JFVH0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1wb3N0aXotZGJ9JwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9kZWZhdWx0OiR7U0VSVklDRV9QQVNTV09SRF9SRURJU31AcmVkaXM6NjM3OScKICAgICAgLSAnQkFDS0VORF9JTlRFUk5BTF9VUkw9aHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICAtICdDTE9VREZMQVJFX0FDQ09VTlRfSUQ9JHtDTE9VREZMQVJFX0FDQ09VTlRfSUR9JwogICAgICAtICdDTE9VREZMQVJFX0FDQ0VTU19LRVk9JHtDTE9VREZMQVJFX0FDQ0VTU19LRVl9JwogICAgICAtICdDTE9VREZMQVJFX1NFQ1JFVF9BQ0NFU1NfS0VZPSR7Q0xPVURGTEFSRV9TRUNSRVRfQUNDRVNTX0tFWX0nCiAgICAgIC0gJ0NMT1VERkxBUkVfQlVDS0VUTkFNRT0ke0NMT1VERkxBUkVfQlVDS0VUTkFNRX0nCiAgICAgIC0gJ0NMT1VERkxBUkVfQlVDS0VUX1VSTD0ke0NMT1VERkxBUkVfQlVDS0VUX1VSTH0nCiAgICAgIC0gJ0NMT1VERkxBUkVfUkVHSU9OPSR7Q0xPVURGTEFSRV9SRUdJT059JwogICAgICAtICdTVE9SQUdFX1BST1ZJREVSPSR7U1RPUkFHRV9QUk9WSURFUjotbG9jYWx9JwogICAgICAtICdVUExPQURfRElSRUNUT1JZPSR7VVBMT0FEX0RJUkVDVE9SWTotL3VwbG9hZHN9JwogICAgICAtICdORVhUX1BVQkxJQ19VUExPQURfRElSRUNUT1JZPSR7TkVYVF9QVUJMSUNfVVBMT0FEX0RJUkVDVE9SWTotL3VwbG9hZHN9JwogICAgICAtICdORVhUX1BVQkxJQ19VUExPQURfU1RBVElDX0RJUkVDVE9SWT0ke05FWFRfUFVCTElDX1VQTE9BRF9TVEFUSUNfRElSRUNUT1JZfScKICAgICAgLSAnUkVTRU5EX0FQSV9LRVk9JHtSRVNFTkRfQVBJX0tFWX0nCiAgICAgIC0gJ0VNQUlMX0ZST01fQUREUkVTUz0ke0VNQUlMX0ZST01fQUREUkVTU30nCiAgICAgIC0gJ0VNQUlMX0ZST01fTkFNRT0ke0VNQUlMX0ZST01fTkFNRX0nCiAgICAgIC0gJ1hfQVBJX0tFWT0ke1NFUlZJQ0VfWF9BUEl9JwogICAgICAtICdYX0FQSV9TRUNSRVQ9JHtTRVJWSUNFX1hfU0VDUkVUfScKICAgICAgLSAnTElOS0VESU5fQ0xJRU5UX0lEPSR7U0VSVklDRV9MSU5LRURJTl9JRH0nCiAgICAgIC0gJ0xJTktFRElOX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX0xJTktFRElOX1NFQ1JFVH0nCiAgICAgIC0gJ1JFRERJVF9DTElFTlRfSUQ9JHtTRVJWSUNFX1JFRERJVF9BUEl9JwogICAgICAtICdSRURESVRfQ0xJRU5UX1NFQ1JFVD0ke1NFUlZJQ0VfUkVERElUX1NFQ1JFVH0nCiAgICAgIC0gJ0dJVEhVQl9DTElFTlRfSUQ9JHtTRVJWSUNFX0dJVEhVQl9JRH0nCiAgICAgIC0gJ0dJVEhVQl9DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9HSVRIVUJfU0VDUkVUfScKICAgICAgLSAnVEhSRUFEU19BUFBfSUQ9JHtTRVJWSUNFX1RIUkVBRFNfSUR9JwogICAgICAtICdUSFJFQURTX0FQUF9TRUNSRVQ9JHtTRVJWSUNFX1RIUkVBRFNfU0VDUkVUfScKICAgICAgLSAnRkFDRUJPT0tfQVBQX0lEPSR7U0VSVklDRV9GQUNFQk9PS19JRH0nCiAgICAgIC0gJ0ZBQ0VCT09LX0FQUF9TRUNSRVQ9JHtTRVJWSUNFX0ZBQ0VCT09LX1NFQ1JFVH0nCiAgICAgIC0gJ1lPVVRVQkVfQ0xJRU5UX0lEPSR7U0VSVklDRV9ZT1VUVUJFX0lEfScKICAgICAgLSAnWU9VVFVCRV9DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9ZT1VUVUJFX1NFQ1JFVH0nCiAgICAgIC0gJ1RJS1RPS19DTElFTlRfSUQ9JHtTRVJWSUNFX1RJS1RPS19JRH0nCiAgICAgIC0gJ1RJS1RPS19DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9USUtUT0tfU0VDUkVUfScKICAgICAgLSAnUElOVEVSRVNUX0NMSUVOVF9JRD0ke1NFUlZJQ0VfUElOVEVSRVNUX0lEfScKICAgICAgLSAnUElOVEVSRVNUX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX1BJTlRFUkVTVF9TRUNSRVR9JwogICAgICAtICdEUklCQkJMRV9DTElFTlRfSUQ9JHtTRVJWSUNFX0RSSUJCTEVfSUR9JwogICAgICAtICdEUklCQkJMRV9DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9EUklCQkxFX1NFQ1JFVH0nCiAgICAgIC0gJ0RJU0NPUkRfQ0xJRU5UX0lEPSR7U0VSVklDRV9ESVNDT1JEX0lEfScKICAgICAgLSAnRElTQ09SRF9DTElFTlRfU0VDUkVUPSR7U0VSVklDRV9ESVNDT1JEX1NFQ1JFVH0nCiAgICAgIC0gJ0RJU0NPUkRfQk9UX1RPS0VOX0lEPSR7U0VSVklDRV9ESVNDT1JEX1RPS0VOfScKICAgICAgLSAnU0xBQ0tfSUQ9JHtTRVJWSUNFX1NMQUNLX0lEfScKICAgICAgLSAnU0xBQ0tfU0VDUkVUPSR7U0VSVklDRV9TTEFDS19TRUNSRVR9JwogICAgICAtICdTTEFDS19TSUdOSU5HX1NFQ1JFVD0ke1NMQUNLX1NJR05JTkdfU0VDUkVUfScKICAgICAgLSAnTUFTVE9ET05fQ0xJRU5UX0lEPSR7U0VSVklDRV9NQVNUT0RPTl9JRH0nCiAgICAgIC0gJ01BU1RPRE9OX0NMSUVOVF9TRUNSRVQ9JHtTRVJWSUNFX01BU1RPRE9OX1NFQ1JFVH0nCiAgICAgIC0gJ0JFRUhJSVZFX0FQSV9LRVk9JHtTRVJWSUNFX0JFRUhJSVZFX0tFWX0nCiAgICAgIC0gJ0JFRUhJSVZFX1BVQkxJQ0FUSU9OX0lEPSR7U0VSVklDRV9CRUVISUlWRV9QVUJJRH0nCiAgICAgIC0gJ09QRU5BSV9BUElfS0VZPSR7U0VSVklDRV9PUEVOQUlfS0VZfScKICAgICAgLSAnTkVYVF9QVUJMSUNfRElTQ09SRF9TVVBQT1JUPSR7TkVYVF9QVUJMSUNfRElTQ09SRF9TVVBQT1JUfScKICAgICAgLSAnTkVYVF9QVUJMSUNfUE9MT1ROTz0ke05FWFRfUFVCTElDX1BPTE9UTk99JwogICAgICAtIElTX0dFTkVSQUw9dHJ1ZQogICAgICAtICdOWF9BRERfUExVR0lOUz0ke05YX0FERF9QTFVHSU5TOi1mYWxzZX0nCiAgICAgIC0gJ0ZFRV9BTU9VTlQ9JHtGRUVfQU1PVU5UOi0wLjA1fScKICAgICAgLSAnU1RSSVBFX1BVQkxJU0hBQkxFX0tFWT0ke1NUUklQRV9QVUJMSVNIQUJMRV9LRVl9JwogICAgICAtICdTVFJJUEVfU0VDUkVUX0tFWT0ke1NUUklQRV9TRUNSRVRfS0VZfScKICAgICAgLSAnU1RSSVBFX1NJR05JTkdfS0VZPSR7U1RSSVBFX1NJR05JTkdfS0VZfScKICAgICAgLSAnU1RSSVBFX1NJR05JTkdfS0VZX0NPTk5FQ1Q9JHtTVFJJUEVfU0lHTklOR19LRVlfQ09OTkVDVH0nCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0aXpfY29uZmlnOi9jb25maWcvJwogICAgICAtICdwb3N0aXpfdXBsb2FkczovdXBsb2Fkcy8nCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovLzEyNy4wLjAuMTo1MDAwLycKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNC41JwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGl6X3Bvc3RncmVzcWxfZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPXBvc3RncmVzCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1wb3N0aXotZGJ9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCOi1wb3N0aXotZGJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjcuMicKICAgIGVudmlyb25tZW50OgogICAgICAtICdSRURJU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgY29tbWFuZDogJ3JlZGlzLXNlcnZlciAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0aXpfcmVkaXNfZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSByZWRpcy1jbGkKICAgICAgICAtICctYScKICAgICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDIwCg==", "tags": [ "post everywhere", "social media", @@ -2958,20 +2959,6 @@ "minversion": "0.0.0", "port": "8000" }, - "windows": { - "documentation": "https://github.com/dockur/windows?utm_source=coolify.io", - "slogan": "Run Windows in a containerized environment.", - "compose": "c2VydmljZXM6CiAgd2luZG93czoKICAgIGltYWdlOiBkb2NrdXJyL3dpbmRvd3MKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3dpbmRvd3Mtc3RvcmFnZTovc3RvcmFnZScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9XSU5ET1dTXzgwMDYKICAgICAgLSBWRVJTSU9OPTExCiAgICBjYXBfYWRkOgogICAgICAtIE5FVF9BRE1JTgogICAgc3RvcF9ncmFjZV9wZXJpb2Q6IDJtCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbmMKICAgICAgICAtICcteicKICAgICAgICAtIDEyNy4wLjAuMQogICAgICAgIC0gJzgwMDYnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTAK", - "tags": [ - "windows", - "virtualization", - "container", - "os" - ], - "logo": "svgs/windows.svg", - "minversion": "0.0.0", - "port": "8006" - }, "wireguard-easy": { "documentation": "https://github.com/wg-easy/wg-easy?utm_source=coolify.io", "slogan": "The easiest way to run WireGuard VPN + Web-based Admin UI.", From 60a723beb65c355150d6278b9593b7a7c19b558a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Mon, 2 Dec 2024 08:22:01 -0500 Subject: [PATCH 34/75] changed to linuxserver base image and updated doc stirng --- templates/compose/plex.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/compose/plex.yaml b/templates/compose/plex.yaml index 0bc6e0334..9ffa15224 100644 --- a/templates/compose/plex.yaml +++ b/templates/compose/plex.yaml @@ -1,12 +1,12 @@ -# documentation: https://github.com/plexinc/pms-docker -# slogan: Plex combines free movies & TV with the best free streaming services, so there’s always more to discover. +# documentation: https://docs.linuxserver.io/images/docker-plex/ +# slogan: Plex organizes video, music and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. # tags: media, server, movies, tv, music # logo: svgs/plex.svg # port: 32400 services: plex: - image: plexinc/pms-docker:latest + image: lscr.io/linuxserver/plex:latest environment: - SERVICE_FQDN_PLEX_32400 - _APP_URL=$SERVICE_FQDN_PLEX @@ -18,8 +18,8 @@ services: # - "/dev/dri:/dev/dri" volumes: - plex-config:/config - - plex-tvshows:/data/tvshows - - plex-movies:/data/movies + - plex-tv:/tv + - plex-movies:/movies healthcheck: test: ["CMD", "curl", "-f", "http://localhost:32400/identity"] interval: 2s From 3c5c0fddbaf22faa1fce98def1ffc4bb2fe2ed40 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 19:06:25 +0100 Subject: [PATCH 35/75] automate some things for Pterodactyl --- templates/compose/pterodactyl.yaml | 49 +++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/templates/compose/pterodactyl.yaml b/templates/compose/pterodactyl.yaml index 19315b4ce..ea64de47a 100644 --- a/templates/compose/pterodactyl.yaml +++ b/templates/compose/pterodactyl.yaml @@ -1,3 +1,4 @@ +# ignore: true # documentation: https://pterodactyl.io/ # slogan: Pterodactyl is a free, open-source game server management panel # tags: game, game server, management, panel, minecraft @@ -41,6 +42,37 @@ services: - "panel-var:/app/var/" - "panel-nginx:/etc/nginx/http.d/" - "panel-certs:/etc/letsencrypt/" + - type: bind + source: ./etc/entrypoint.sh + target: /entrypoint.sh + mode: "0755" + content: | + #!/bin/sh + set -e + + echo "Waiting for services to be ready..." + sleep 30 + + echo "Setting logs permissions..." + chown -R nginx: /app/storage/logs/ + + if ! php artisan p:user:list | grep -q "$ADMIN_EMAIL"; then + echo "Creating admin user..." + php artisan p:user:make --no-interaction \ + --admin=1 \ + --email="$ADMIN_EMAIL" \ + --username="$ADMIN_USERNAME" \ + --name-first="$ADMIN_FIRSTNAME" \ + --name-last="$ADMIN_LASTNAME" \ + --password="$ADMIN_PASSWORD" + echo "Admin user created" + else + echo "Admin user already exists, skipping creation" + fi + + exec supervisord -c --nodaemon + + command: ["/entrypoint.sh"] healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:80 || exit 1"] interval: 10s @@ -48,23 +80,25 @@ services: retries: 3 environment: - SERVICE_FQDN_PTERODACTYL - + - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com} + - ADMIN_USERNAME=${SERVICE_USER_ADMIN} + - ADMIN_FIRSTNAME=${ADMIN_FIRSTNAME:-Admin} + - ADMIN_LASTNAME=${ADMIN_LASTNAME:-User} + - ADMIN_PASSWORD=${SERVICE_PASSWORD_ADMIN} + - PTERODACTYL_HTTPS=${PTERODACTYL_HTTPS:-false} - APP_ENV=production - APP_ENVIRONMENT_ONLY=false - APP_URL=${PTERODACTYL_PUBLIC_FQDN:-$SERVICE_FQDN_PTERODACTYL} - APP_TIMEZONE=${TIMEZONE:-UTC} - APP_SERVICE_AUTHOR=${APP_SERVICE_AUTHOR:-author@example.com} - LOG_LEVEL=${LOG_LEVEL:-debug} - - CACHE_DRIVER=redis - SESSION_DRIVER=redis - QUEUE_DRIVER=redis - REDIS_HOST=redis - - DB_HOST=mariadb - DB_PORT=3306 - DB_PASSWORD=$SERVICE_PASSWORD_MYSQL - - MAIL_FROM=$MAIL_FROM - MAIL_DRIVER=$MAIL_DRIVER - MAIL_HOST=$MAIL_HOST @@ -111,10 +145,3 @@ services: v6: subnet: fdba:17c8:6c94::/64 gateway: fdba:17c8:6c94::1011 - -volumes: - panel-var: - panel-nginx: - panel-certs: - wings-logs: - pterodactyl-db: From b08f32508c1919d8f7da89522dc325ffcf427048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Mon, 2 Dec 2024 13:14:00 -0500 Subject: [PATCH 36/75] added sonarr --- public/svgs/sonarr.svg | 1 + templates/compose/sonarr.yaml | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 public/svgs/sonarr.svg create mode 100644 templates/compose/sonarr.yaml diff --git a/public/svgs/sonarr.svg b/public/svgs/sonarr.svg new file mode 100644 index 000000000..91c04e289 --- /dev/null +++ b/public/svgs/sonarr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/compose/sonarr.yaml b/templates/compose/sonarr.yaml new file mode 100644 index 000000000..c2ac3e270 --- /dev/null +++ b/templates/compose/sonarr.yaml @@ -0,0 +1,24 @@ +# documentation: https://hub.docker.com/r/linuxserver/sonarr +# slogan: Sonarr⁠ (formerly NZBdrone) is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available. +# tags: media, server, tv +# logo: svgs/sonarr.svg +# port: 8989 + +services: + sonarr: + image: lscr.io/linuxserver/sonarr:latest + environment: + - SERVICE_FQDN_SONARR_8989 + - _APP_URL=$SERVICE_FQDN_SONARR + - PUID=1000 + - PGID=1000 + - TZ=${TZ:-America/Toronto} + volumes: + - sonarr-config:/config + # - sonarr-tv:/tv #optional + # - downloads:/downloads #optional + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8989/ping"] + interval: 2s + timeout: 10s + retries: 15 From 63fb9e9f40843adc60caadbee0bbe403f0bc84b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Mon, 2 Dec 2024 13:18:35 -0500 Subject: [PATCH 37/75] added radarr --- public/svgs/radarr.svg | 1 + templates/compose/radarr.yaml | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 public/svgs/radarr.svg create mode 100644 templates/compose/radarr.yaml diff --git a/public/svgs/radarr.svg b/public/svgs/radarr.svg new file mode 100644 index 000000000..93a4c9232 --- /dev/null +++ b/public/svgs/radarr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/compose/radarr.yaml b/templates/compose/radarr.yaml new file mode 100644 index 000000000..2795582a6 --- /dev/null +++ b/templates/compose/radarr.yaml @@ -0,0 +1,24 @@ +# documentation: https://hub.docker.com/r/linuxserver/radarr +# slogan: Radarr⁠ - A fork of Sonarr to work with movies à la Couchpotato. +# tags: media, server, movies +# logo: svgs/radarr.svg +# port: 7878 + +services: + radarr: + image: lscr.io/linuxserver/radarr:latest + environment: + - SERVICE_FQDN_RADARR_7878 + - _APP_URL=$SERVICE_FQDN_RADARR + - PUID=1000 + - PGID=1000 + - TZ=${TZ:-America/Toronto} + volumes: + - radarr-config:/config + # - radarr-movies:/movies #optional + # - downloads:/downloads #optional + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:7878/ping"] + interval: 2s + timeout: 10s + retries: 15 From 59fe9de1c932e06e189c5fe8c2d16e8b0ebfc6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Tremblay=20Lefran=C3=A7ois?= Date: Mon, 2 Dec 2024 13:23:55 -0500 Subject: [PATCH 38/75] added prowlarr --- public/svgs/prowlarr.svg | 1 + templates/compose/prowlarr.yaml | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 public/svgs/prowlarr.svg create mode 100644 templates/compose/prowlarr.yaml diff --git a/public/svgs/prowlarr.svg b/public/svgs/prowlarr.svg new file mode 100644 index 000000000..a1a5a8ce3 --- /dev/null +++ b/public/svgs/prowlarr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/compose/prowlarr.yaml b/templates/compose/prowlarr.yaml new file mode 100644 index 000000000..f173ee1ca --- /dev/null +++ b/templates/compose/prowlarr.yaml @@ -0,0 +1,22 @@ +# documentation: https://hub.docker.com/r/linuxserver/prowlarr +# slogan: Prowlarr⁠ is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all). +# tags: media, server, movies, tv, indexer, torrent, nzb, usenet +# logo: svgs/prowlarr.svg +# port: 9696 + +services: + prowlarr: + image: lscr.io/linuxserver/prowlarr:latest + environment: + - SERVICE_FQDN_PROWLARR_9696 + - _APP_URL=$SERVICE_FQDN_PROWLARR + - PUID=1000 + - PGID=1000 + - TZ=${TZ:-America/Toronto} + volumes: + - prowlarr-config:/config + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9696/ping"] + interval: 2s + timeout: 10s + retries: 15 From f9143d0500af40d27d3a0d354f5e901169040644 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:14:56 +0100 Subject: [PATCH 39/75] Update service-templates.json --- templates/service-templates.json | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/templates/service-templates.json b/templates/service-templates.json index 0fff92f86..363fe511f 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -2285,6 +2285,24 @@ "minversion": "0.0.0", "port": "8080" }, + "prowlarr": { + "documentation": "https://hub.docker.com/r/linuxserver/prowlarr?utm_source=coolify.io", + "slogan": "Prowlarr\u2060 is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all).", + "compose": "c2VydmljZXM6CiAgcHJvd2xhcnI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcHJvd2xhcnI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BST1dMQVJSXzk2OTYKICAgICAgLSBfQVBQX1VSTD0kU0VSVklDRV9GUUROX1BST1dMQVJSCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gJ1RaPSR7VFo6LUFtZXJpY2EvVG9yb250b30nCiAgICB2b2x1bWVzOgogICAgICAtICdwcm93bGFyci1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo5Njk2L3BpbmcnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", + "tags": [ + "media", + "server", + "movies", + "tv", + "indexer", + "torrent", + "nzb", + "usenet" + ], + "logo": "svgs/prowlarr.svg", + "minversion": "0.0.0", + "port": "9696" + }, "qbittorrent": { "documentation": "https://docs.linuxserver.io/images/docker-qbittorrent/?utm_source=coolify.io", "slogan": "The qBittorrent project aims to provide an open-source software alternative to \u03bcTorrent.", @@ -2333,6 +2351,19 @@ "minversion": "0.0.0", "port": "15672" }, + "radarr": { + "documentation": "https://hub.docker.com/r/linuxserver/radarr?utm_source=coolify.io", + "slogan": "Radarr\u2060 - A fork of Sonarr to work with movies \u00e0 la Couchpotato.", + "compose": "c2VydmljZXM6CiAgcmFkYXJyOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3JhZGFycjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUkFEQVJSXzc4NzgKICAgICAgLSBfQVBQX1VSTD0kU0VSVklDRV9GUUROX1JBREFSUgogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtICdUWj0ke1RaOi1BbWVyaWNhL1Rvcm9udG99JwogICAgdm9sdW1lczoKICAgICAgLSAncmFkYXJyLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0Ojc4NzgvcGluZycKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=", + "tags": [ + "media", + "server", + "movies" + ], + "logo": "svgs/radarr.svg", + "minversion": "0.0.0", + "port": "7878" + }, "reactive-resume": { "documentation": "https://rxresu.me/?utm_source=coolify.io", "slogan": "A one-of-a-kind resume builder that keeps your privacy in mind.", @@ -2473,6 +2504,19 @@ "minversion": "0.0.0", "port": "6001" }, + "sonarr": { + "documentation": "https://hub.docker.com/r/linuxserver/sonarr?utm_source=coolify.io", + "slogan": "Sonarr\u2060 (formerly NZBdrone) is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.", + "compose": "c2VydmljZXM6CiAgc29uYXJyOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3NvbmFycjpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU09OQVJSXzg5ODkKICAgICAgLSBfQVBQX1VSTD0kU0VSVklDRV9GUUROX1NPTkFSUgogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtICdUWj0ke1RaOi1BbWVyaWNhL1Rvcm9udG99JwogICAgdm9sdW1lczoKICAgICAgLSAnc29uYXJyLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0Ojg5ODkvcGluZycKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=", + "tags": [ + "media", + "server", + "tv" + ], + "logo": "svgs/sonarr.svg", + "minversion": "0.0.0", + "port": "8989" + }, "statusnook": { "documentation": "https://statusnook.com?utm_source=coolify.io", "slogan": "Effortlessly deploy a status page and start monitoring endpoints in minutes", From a776871b22ffdb6d5c577f5f5dd3bde93f763888 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:35:04 +0100 Subject: [PATCH 40/75] disable coder see #4431 --- templates/compose/coder.yaml | 1 + templates/service-templates.json | 15 --------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/templates/compose/coder.yaml b/templates/compose/coder.yaml index fec22b5bf..717cff03c 100644 --- a/templates/compose/coder.yaml +++ b/templates/compose/coder.yaml @@ -1,3 +1,4 @@ +# ignore: true # documentation: https://coder.com/docs # slogan: Coder is an open-source platform for creating and managing cloud development environments on your infrastructure, with the tools and IDEs your developers already love. # tags: coder,development,environment,self-hosted,postgres diff --git a/templates/service-templates.json b/templates/service-templates.json index 363fe511f..222ca1601 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -418,21 +418,6 @@ "minversion": "0.0.0", "port": "8443" }, - "coder": { - "documentation": "https://coder.com/docs?utm_source=coolify.io", - "slogan": "Coder is an open-source platform for creating and managing cloud development environments on your infrastructure, with the tools and IDEs your developers already love.", - "compose": "c2VydmljZXM6CiAgY29kZXI6CiAgICBpbWFnZTogJ2doY3IuaW8vY29kZXIvY29kZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVSXzcwODAKICAgICAgLSAnQ09ERVJfUEdfQ09OTkVDVElPTl9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QGNvZGVyLWRhdGFiYXNlLyR7UE9TVEdSRVNfREI6LWNvZGVyLWRifT9zc2xtb2RlPWRpc2FibGUnCiAgICAgIC0gJ0NPREVSX0hUVFBfQUREUkVTUz0wLjAuMC4wOjcwODAnCiAgICAgIC0gJ0NPREVSX0FDQ0VTU19VUkw9JHtTRVJWSUNFX0ZRRE5fQ09ERVJ9JwogICAgdm9sdW1lczoKICAgICAgLSAnL3Zhci9ydW4vZG9ja2VyLnNvY2s6L3Zhci9ydW4vZG9ja2VyLnNvY2snCiAgICBkZXBlbmRzX29uOgogICAgICBjb2Rlci1kYXRhYmFzZToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjcwODAnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICBjb2Rlci1kYXRhYmFzZToKICAgIGltYWdlOiAncG9zdGdyZXM6MTYuNC1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgUE9TVEdSRVNfVVNFUjogJyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgUE9TVEdSRVNfUEFTU1dPUkQ6ICcke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICBQT1NUR1JFU19EQjogJyR7UE9TVEdSRVNfREI6LWNvZGVyLWRifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NvZGVyLXBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICR7UE9TVEdSRVNfVVNFUjotdXNlcm5hbWV9IC1kICR7UE9TVEdSRVNfREI6LWNvZGVyfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUK", - "tags": [ - "coder", - "development", - "environment", - "self-hosted", - "postgres" - ], - "logo": "svgs/coder.svg", - "minversion": "0.0.0", - "port": "7080" - }, "cryptgeon": { "documentation": "https://github.com/cupcakearmy/cryptgeon?utm_source=coolify.io", "slogan": "Secure note / file sharing service inspired by PrivNote.", From d1a1ecbe28ce128173473bd6ba63158d8462455c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 22:01:15 +0100 Subject: [PATCH 41/75] fix: only able to select the right keys --- app/Livewire/Boarding/Index.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index b6c799c4e..eadabba7c 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -172,13 +172,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== public function getProxyType() { - // Set Default Proxy Type $this->selectProxy(ProxyTypes::TRAEFIK->value); - // $proxyTypeSet = $this->createdServer->proxy->type; - // if (!$proxyTypeSet) { - // $this->currentState = 'select-proxy'; - // return; - // } $this->getProjects(); } @@ -189,7 +183,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== return; } - $this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey); + $this->createdPrivateKey = PrivateKey::where('team_id', currentTeam()->id)->where('id', $this->selectedExistingPrivateKey)->first(); $this->privateKey = $this->createdPrivateKey->private_key; $this->currentState = 'create-server'; } From ec1312a3f8895ac2768ec2c79dceaa30acb2ecc8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 22:12:02 +0100 Subject: [PATCH 42/75] able to change zoom --- resources/css/app.css | 16 +-------- resources/views/components/navbar.blade.php | 39 +++++++++++++++++++-- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/resources/css/app.css b/resources/css/app.css index c3d46cc04..32d476c1a 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -4,20 +4,6 @@ @tailwind components; @tailwind utilities; -html { - font-size: 93.75%; -} - -:root { - --vh: 1vh; -} - -@screen lg { - html { - font-size: 87.5%; - } -} - html, body { @apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400 w-full; @@ -336,4 +322,4 @@ section { .dz-button { @apply w-full p-4 py-10 my-4 font-bold bg-white border dark:border-coolgray-400 dark:text-white dark:bg-transparent hover:dark:bg-coolgray-400; -} +} \ No newline at end of file diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index f635a6787..7d343c645 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -7,8 +7,13 @@ } window.location.reload(); }, + setZoom(zoom) { + localStorage.setItem('zoom', zoom); + window.location.reload(); + }, init() { this.full = localStorage.getItem('pageWidth'); + this.zoom = localStorage.getItem('zoom'); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { const userSettings = localStorage.getItem('theme'); if (userSettings !== 'system') { @@ -21,6 +26,7 @@ } }); this.queryTheme(); + this.checkZoom(); }, setTheme(type) { this.theme = type; @@ -44,6 +50,30 @@ this.theme = 'system'; document.documentElement.classList.remove('dark'); } + }, + checkZoom() { + if (this.zoom === null) { + this.setZoom(100); + } + if (this.zoom === '90') { + const style = document.createElement('style'); + style.textContent = ` + html { + font-size: 93.75%; + } + + :root { + --vh: 1vh; + } + + @media (min-width: 1024px) { + html { + font-size: 87.5%; + } + } + `; + document.head.appendChild(style); + } } }">
@@ -69,7 +99,7 @@
-
Color
+
Color
@@ -78,6 +108,9 @@ x-show="full === 'full'">Center +
Zoom
+ +
@@ -163,8 +196,8 @@ class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}" href="{{ route('storage.index') }}"> - + From 7f449c3b729c9d89b3c7c86383ba5197fdca1fd8 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:37:11 +0100 Subject: [PATCH 43/75] fix: test email should not be required --- app/Livewire/Notifications/Email.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index fcedf1305..c4e4aae14 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -73,8 +73,8 @@ class Email extends Component #[Validate(['nullable', 'string'])] public ?string $resendApiKey = null; - #[Validate(['required', 'email'])] - public string $testEmailAddress = ''; + #[Validate(['nullable', 'email'])] + public ?string $testEmailAddress = null; public function mount() { From 58988d3686cafe60b30a8c5679772a38f39cb3ad Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 22:49:41 +0100 Subject: [PATCH 44/75] fix: a few inputs --- app/Actions/Docker/GetContainersStatus.php | 10 +- app/Actions/Server/ServerCheck.php | 8 +- .../Api/ApplicationsController.php | 55 +++++- .../Controllers/Api/DatabasesController.php | 21 ++- .../Controllers/Api/ServicesController.php | 4 +- app/Jobs/PushServerUpdateJob.php | 2 +- app/Models/BaseModel.php | 13 ++ app/Models/StandalonePostgresql.php | 16 +- app/Models/Team.php | 7 + bootstrap/helpers/shared.php | 156 ++++++++++-------- 10 files changed, 190 insertions(+), 102 deletions(-) diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index 8aae910a9..e9f577e06 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -179,7 +179,7 @@ class GetContainersStatus })->first(); if (! $foundTcpProxy) { StartDatabaseProxy::run($database); - // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for database", $this->server)); } } } else { @@ -260,7 +260,7 @@ class GetContainersStatus $environmentName = data_get($service, 'environment.name'); if ($projectUuid && $serviceUuid && $environmentName) { - $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid; + $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/service/' . $serviceUuid; } else { $url = null; } @@ -286,7 +286,7 @@ class GetContainersStatus $environment = data_get($application, 'environment.name'); if ($projectUuid && $applicationUuid && $environment) { - $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid; + $url = base_url() . '/project/' . $projectUuid . '/' . $environment . '/application/' . $applicationUuid; } else { $url = null; } @@ -311,7 +311,7 @@ class GetContainersStatus $applicationUuid = data_get($preview, 'application.uuid'); if ($projectUuid && $applicationUuid && $environmentName) { - $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid; + $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/application/' . $applicationUuid; } else { $url = null; } @@ -336,7 +336,7 @@ class GetContainersStatus $databaseUuid = data_get($database, 'uuid'); if ($projectUuid && $databaseUuid && $environmentName) { - $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid; + $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/database/' . $databaseUuid; } else { $url = null; } diff --git a/app/Actions/Server/ServerCheck.php b/app/Actions/Server/ServerCheck.php index 5f9a1e357..35b4996b4 100644 --- a/app/Actions/Server/ServerCheck.php +++ b/app/Actions/Server/ServerCheck.php @@ -51,7 +51,6 @@ class ServerCheck $containerReplicates = null; $this->isSentinel = true; - } else { ['containers' => $this->containers, 'containerReplicates' => $containerReplicates] = $this->server->getContainers(); // ServerStorageCheckJob::dispatch($this->server); @@ -148,7 +147,6 @@ class ServerCheck } else { $labels = Arr::undot(data_get($container, 'Config.Labels')); } - } $managed = data_get($labels, 'coolify.managed'); if (! $managed) { @@ -215,7 +213,7 @@ class ServerCheck if ($isPublic) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { if ($this->isSentinel) { - return data_get($value, 'name') === $uuid.'-proxy'; + return data_get($value, 'name') === $uuid . '-proxy'; } else { if ($this->server->isSwarm()) { @@ -247,7 +245,7 @@ class ServerCheck if ($isPublic) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { if ($this->isSentinel) { - return data_get($value, 'name') === $uuid.'-proxy'; + return data_get($value, 'name') === $uuid . '-proxy'; } else { if ($this->server->isSwarm()) { return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; @@ -259,7 +257,7 @@ class ServerCheck })->first(); if (! $foundTcpProxy) { StartDatabaseProxy::run($database); - // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for database", $this->server)); } } } diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index c69640970..614208c78 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1591,16 +1591,32 @@ class ApplicationsController extends Controller } $domains = $request->domains; if ($request->has('domains') && $server->isProxyShouldRun()) { - $errors = []; + $uuid = $request->uuid; $fqdn = $request->domains; $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); $fqdn = str($fqdn)->replaceStart(',', '')->trim(); - $application->fqdn = $fqdn; - if (! $application->settings->is_container_label_readonly_enabled) { - $customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); - $application->custom_labels = base64_encode($customLabels); + $errors = []; + $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { + $domain = trim($domain); + if (filter_var($domain, FILTER_VALIDATE_URL) === false || !preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}/', $domain)) { + $errors[] = 'Invalid domain: '.$domain; + } + return $domain; + }); + if (count($errors) > 0) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + if (checkIfDomainIsAlreadyUsed($fqdn, $teamId, $uuid)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'domains' => 'One of the domain is already used.', + ], + ], 422); } - $request->offsetUnset('domains'); } $dockerComposeDomainsJson = collect(); @@ -2811,3 +2827,30 @@ class ApplicationsController extends Controller } } } + + $fqdn = str($fqdn)->replaceStart(',', '')->trim(); + $errors = []; + $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { + if (filter_var($domain, FILTER_VALIDATE_URL) === false) { + $errors[] = 'Invalid domain: ' . $domain; + } + + return str($domain)->trim()->lower(); + }); + if (count($errors) > 0) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + if (checkIfDomainIsAlreadyUsed($fqdn, $teamId, $uuid)) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => [ + 'domains' => 'One of the domain is already used.', + ], + ], 422); + } + } + } +} diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index ce658d2a2..9366e6300 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -1557,7 +1557,8 @@ class DatabasesController extends Controller ] ) ), - ]), + ] + ), new OA\Response( response: 401, ref: '#/components/responses/401', @@ -1632,9 +1633,11 @@ class DatabasesController extends Controller type: 'object', properties: [ 'message' => ['type' => 'string', 'example' => 'Database starting request queued.'], - ]) + ] + ) ), - ]), + ] + ), new OA\Response( response: 401, ref: '#/components/responses/401', @@ -1708,9 +1711,11 @@ class DatabasesController extends Controller type: 'object', properties: [ 'message' => ['type' => 'string', 'example' => 'Database stopping request queued.'], - ]) + ] + ) ), - ]), + ] + ), new OA\Response( response: 401, ref: '#/components/responses/401', @@ -1784,9 +1789,11 @@ class DatabasesController extends Controller type: 'object', properties: [ 'message' => ['type' => 'string', 'example' => 'Database restaring request queued.'], - ]) + ] + ) ), - ]), + ] + ), new OA\Response( response: 401, ref: '#/components/responses/401', diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index bf90322e2..abf8a24af 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -308,7 +308,7 @@ class ServicesController extends Controller } if ($oneClickService) { $service_payload = [ - 'name' => "$oneClickServiceName-".str()->random(10), + 'name' => "$oneClickServiceName-" . str()->random(10), 'docker_compose_raw' => base64_decode($oneClickService), 'environment_id' => $environment->id, 'service_type' => $oneClickServiceName, @@ -320,7 +320,7 @@ class ServicesController extends Controller data_set($service_payload, 'connect_to_docker_network', true); } $service = Service::create($service_payload); - $service->name = "$oneClickServiceName-".$service->uuid; + $service->name = "$oneClickServiceName-" . $service->uuid; $service->save(); if ($oneClickDotEnvs?->count() > 0) { $oneClickDotEnvs->each(function ($value) use ($service) { diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index 24f8d1e6b..fa146ff96 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -280,7 +280,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue })->first(); if (! $tcpProxyContainerFound) { StartDatabaseProxy::dispatch($database); - $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); + $this->server->team?->notify(new ContainerRestarted("TCP Proxy for database", $this->server)); } else { } } diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 17201ea6e..500c34fb6 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Visus\Cuid2\Cuid2; @@ -18,4 +19,16 @@ abstract class BaseModel extends Model } }); } + public function name(): Attribute + { + return new Attribute( + get: fn() => sanitize_string($this->getRawOriginal('name')), + ); + } + public function image(): Attribute + { + return new Attribute( + get: fn() => sanitize_string($this->getRawOriginal('image')), + ); + } } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 4a457a6cf..99cc8a55b 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -25,7 +25,7 @@ class StandalonePostgresql extends BaseModel { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'postgres-data-'.$database->uuid, + 'name' => 'postgres-data-' . $database->uuid, 'mount_path' => '/var/lib/postgresql/data', 'host_path' => null, 'resource_id' => $database->id, @@ -48,7 +48,7 @@ class StandalonePostgresql extends BaseModel public function workdir() { - return database_configuration_dir()."/{$this->uuid}"; + return database_configuration_dir() . "/{$this->uuid}"; } protected function serverStatus(): Attribute @@ -65,7 +65,7 @@ class StandalonePostgresql extends BaseModel $server = data_get($this, 'destination.server'); $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { - instant_remote_process(['rm -rf '.$this->workdir()], $server, false); + instant_remote_process(['rm -rf ' . $this->workdir()], $server, false); } } @@ -82,7 +82,7 @@ class StandalonePostgresql extends BaseModel public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image.$this->ports_mappings.$this->postgres_initdb_args.$this->postgres_host_auth_method; + $newConfigHash = $this->image . $this->ports_mappings . $this->postgres_initdb_args . $this->postgres_host_auth_method; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -186,14 +186,14 @@ class StandalonePostgresql extends BaseModel public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === '' ? null : $value, + set: fn($value) => $value === '' ? null : $value, ); } public function portsMappingsArray(): Attribute { return Attribute::make( - get: fn () => is_null($this->ports_mappings) + get: fn() => is_null($this->ports_mappings) ? [] : explode(',', $this->ports_mappings), @@ -208,7 +208,7 @@ class StandalonePostgresql extends BaseModel public function databaseType(): Attribute { return new Attribute( - get: fn () => $this->type(), + get: fn() => $this->type(), ); } @@ -220,7 +220,7 @@ class StandalonePostgresql extends BaseModel protected function internalDbUrl(): Attribute { return new Attribute( - get: fn () => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}", + get: fn() => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}", ); } diff --git a/app/Models/Team.php b/app/Models/Team.php index e21aa3a25..76d89fb8e 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -127,6 +127,13 @@ class Team extends Model implements SendsDiscord, SendsEmail ]; } + public function name(): Attribute + { + return new Attribute( + get: fn() => sanitize_string($this->getRawOriginal('name')), + ); + } + public function getRecepients($notification) { $recipients = data_get($notification, 'emails', null); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 1a943cd73..ddc578400 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -67,31 +67,51 @@ function base_configuration_dir(): string } function application_configuration_dir(): string { - return base_configuration_dir().'/applications'; + return base_configuration_dir() . '/applications'; } function service_configuration_dir(): string { - return base_configuration_dir().'/services'; + return base_configuration_dir() . '/services'; } function database_configuration_dir(): string { - return base_configuration_dir().'/databases'; + return base_configuration_dir() . '/databases'; } function database_proxy_dir($uuid): string { - return base_configuration_dir()."/databases/$uuid/proxy"; + return base_configuration_dir() . "/databases/$uuid/proxy"; } function backup_dir(): string { - return base_configuration_dir().'/backups'; + return base_configuration_dir() . '/backups'; } function metrics_dir(): string { - return base_configuration_dir().'/metrics'; + return base_configuration_dir() . '/metrics'; +} + +function sanitize_string(string $input): string +{ + // Remove any HTML/PHP tags + $sanitized = strip_tags($input); + + // Convert special characters to HTML entities + $sanitized = htmlspecialchars($sanitized, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // Remove any control characters + $sanitized = preg_replace('/[\x00-\x1F\x7F]/u', '', $sanitized); + + // Trim whitespace + $sanitized = trim($sanitized); + + return $sanitized; } function generate_readme_file(string $name, string $updated_at): string { + $name = sanitize_string($name); + $updated_at = sanitize_string($updated_at); + return "Resource name: $name\nLatest Deployment Date: $updated_at"; } @@ -122,8 +142,8 @@ function refreshSession(?Team $team = null): void $team = User::find(Auth::id())->teams->first(); } } - Cache::forget('team:'.Auth::id()); - Cache::remember('team:'.Auth::id(), 3600, function () use ($team) { + Cache::forget('team:' . Auth::id()); + Cache::remember('team:' . Auth::id(), 3600, function () use ($team) { return $team; }); session(['currentTeam' => $team]); @@ -155,7 +175,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n $message = null; } if ($customErrorMessage) { - $message = $customErrorMessage.' '.$message; + $message = $customErrorMessage . ' ' . $message; } if (isset($livewire)) { @@ -410,7 +430,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null Mail::send( [], [], - fn (Message $message) => $message + fn(Message $message) => $message ->to($email) ->replyTo($email) ->cc($cc) @@ -421,7 +441,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null Mail::send( [], [], - fn (Message $message) => $message + fn(Message $message) => $message ->to($email) ->subject($mail->subject) ->html((string) $mail->render()) @@ -657,19 +677,19 @@ function queryResourcesByUuid(string $uuid) function generateTagDeployWebhook($tag_name) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl).'/api/v1'; + $api = Url::fromString($baseUrl) . '/api/v1'; $endpoint = "/deploy?tag=$tag_name"; - return $api.$endpoint; + return $api . $endpoint; } function generateDeployWebhook($resource) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl).'/api/v1'; + $api = Url::fromString($baseUrl) . '/api/v1'; $endpoint = '/deploy'; $uuid = data_get($resource, 'uuid'); - return $api.$endpoint."?uuid=$uuid&force=false"; + return $api . $endpoint . "?uuid=$uuid&force=false"; } function generateGitManualWebhook($resource, $type) { @@ -679,7 +699,7 @@ function generateGitManualWebhook($resource, $type) if ($resource->getMorphClass() === \App\Models\Application::class) { $baseUrl = base_url(); - return Url::fromString($baseUrl)."/webhooks/source/$type/events/manual"; + return Url::fromString($baseUrl) . "/webhooks/source/$type/events/manual"; } return null; @@ -1047,7 +1067,7 @@ function validate_dns_entry(string $fqdn, Server $server) $query = new DNSQuery($dns_server); $results = $query->query($host, $type); if ($results === false || $query->hasError()) { - ray('Error: '.$query->getLasterror()); + ray('Error: ' . $query->getLasterror()); } else { foreach ($results as $result) { if ($result->getType() == $type) { @@ -1057,7 +1077,7 @@ function validate_dns_entry(string $fqdn, Server $server) break; } if ($result->getData() === $ip) { - ray($host.' has IP address '.$result->getData()); + ray($host . ' has IP address ' . $result->getData()); ray($result->getString()); $found_matching_ip = true; break; @@ -1105,15 +1125,15 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = $applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']); $serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']); if ($uuid) { - $applications = $applications->filter(fn ($app) => $app->uuid !== $uuid); - $serviceApplications = $serviceApplications->filter(fn ($app) => $app->uuid !== $uuid); + $applications = $applications->filter(fn($app) => $app->uuid !== $uuid); + $serviceApplications = $serviceApplications->filter(fn($app) => $app->uuid !== $uuid); } $domainFound = false; foreach ($applications as $app) { if (is_null($app->fqdn)) { continue; } - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1132,7 +1152,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = if (str($app->fqdn)->isEmpty()) { continue; } - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1182,7 +1202,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null }); $apps = Application::all(); foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1201,7 +1221,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null } $apps = ServiceApplication::all(); foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1258,7 +1278,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array $commands = $commands->map(function ($line) use ($server) { if (Str::startsWith($line, 'sudo mkdir -p')) { - return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p'); + return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p'); } return $line; @@ -1290,7 +1310,7 @@ function parseLineForSudo(string $command, Server $server): string $command = "sudo $command"; } if (Str::startsWith($command, 'sudo mkdir -p')) { - $command = "$command && sudo chown -R $server->user:$server->user ".Str::after($command, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($command, 'sudo mkdir -p'); + $command = "$command && sudo chown -R $server->user:$server->user " . Str::after($command, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($command, 'sudo mkdir -p'); } if (str($command)->contains('$(') || str($command)->contains('`')) { $command = str($command)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value(); @@ -1427,9 +1447,9 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull return $volume; } if (get_class($resource) === \App\Models\Application::class) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; + $dir = base_configuration_dir() . '/applications/' . $resource->uuid; } else { - $dir = base_configuration_dir().'/services/'.$resource->service->uuid; + $dir = base_configuration_dir() . '/services/' . $resource->service->uuid; } if ($source->startsWith('.')) { @@ -1439,7 +1459,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull $source = $source->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $source = $source."-pr-$pull_request_id"; + $source = $source . "-pr-$pull_request_id"; } if (! $resource?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git) { LocalFileVolume::updateOrCreate( @@ -1897,7 +1917,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (! $isDatabase) { if ($savedService->fqdn) { - data_set($savedService, 'fqdn', $savedService->fqdn.','.$fqdn); + data_set($savedService, 'fqdn', $savedService->fqdn . ',' . $fqdn); } else { data_set($savedService, 'fqdn', $fqdn); } @@ -1957,7 +1977,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($resource->server, $containerName); } else { - $fqdn = generateFqdn($resource->server, Str::lower($forService).'-'.$resource->uuid); + $fqdn = generateFqdn($resource->server, Str::lower($forService) . '-' . $resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -2299,7 +2319,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; + $dir = base_configuration_dir() . '/applications/' . $resource->uuid; if ($name->startsWith('.')) { $name = $name->replaceFirst('.', $dir); } @@ -2307,12 +2327,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $name->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; + $name = $name . "-pr-$pull_request_id"; } $volume = str("$name:$mount"); } else { if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; + $name = $name . "-pr-$pull_request_id"; $volume = str("$name:$mount"); if ($topLevelVolumes->has($name)) { $v = $topLevelVolumes->get($name); @@ -2351,7 +2371,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; + $name = $name . "-pr-$pull_request_id"; } $volume = str("$name:$mount"); } @@ -2362,7 +2382,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $read_only = data_get($volume, 'read_only'); if ($source && $target) { if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; + $dir = base_configuration_dir() . '/applications/' . $resource->uuid; if (str($source, '.')) { $source = str($source)->replaceFirst('.', $dir); } @@ -2370,21 +2390,21 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $source = str($source)->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $source = $source."-pr-$pull_request_id"; + $source = $source . "-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); + data_set($volume, 'source', $source . ':' . $target . ':ro'); } else { - data_set($volume, 'source', $source.':'.$target); + data_set($volume, 'source', $source . ':' . $target); } } else { if ($pull_request_id !== 0) { - $source = $source."-pr-$pull_request_id"; + $source = $source . "-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); + data_set($volume, 'source', $source . ':' . $target . ':ro'); } else { - data_set($volume, 'source', $source.':'.$target); + data_set($volume, 'source', $source . ':' . $target); } if (! str($source)->startsWith('/')) { if ($topLevelVolumes->has($source)) { @@ -2423,7 +2443,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; + $dir = base_configuration_dir() . '/applications/' . $resource->uuid; if ($name->startsWith('.')) { $name = $name->replaceFirst('.', $dir); } @@ -2431,13 +2451,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $name->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; + $name = $name . "-pr-$pull_request_id"; } $volume = str("$name:$mount"); } else { if ($pull_request_id !== 0) { $uuid = $resource->uuid; - $name = $uuid."-$name-pr-$pull_request_id"; + $name = $uuid . "-$name-pr-$pull_request_id"; $volume = str("$name:$mount"); if ($topLevelVolumes->has($name)) { $v = $topLevelVolumes->get($name); @@ -2456,7 +2476,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } else { $uuid = $resource->uuid; - $name = str($uuid."-$name"); + $name = str($uuid . "-$name"); $volume = str("$name:$mount"); if ($topLevelVolumes->has($name->value())) { $v = $topLevelVolumes->get($name->value()); @@ -2479,7 +2499,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; + $name = $name . "-pr-$pull_request_id"; } $volume = str("$name:$mount"); } @@ -2491,7 +2511,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($source && $target) { $uuid = $resource->uuid; if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; + $dir = base_configuration_dir() . '/applications/' . $resource->uuid; if (str($source, '.')) { $source = str($source)->replaceFirst('.', $dir); } @@ -2499,20 +2519,20 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $source = str($source)->replaceFirst('~', $dir); } if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); + data_set($volume, 'source', $source . ':' . $target . ':ro'); } else { - data_set($volume, 'source', $source.':'.$target); + data_set($volume, 'source', $source . ':' . $target); } } else { if ($pull_request_id === 0) { - $source = $uuid."-$source"; + $source = $uuid . "-$source"; } else { - $source = $uuid."-$source-pr-$pull_request_id"; + $source = $uuid . "-$source-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); + data_set($volume, 'source', $source . ':' . $target . ':ro'); } else { - data_set($volume, 'source', $source.':'.$target); + data_set($volume, 'source', $source . ':' . $target); } if (! str($source)->startsWith('/')) { if ($topLevelVolumes->has($source)) { @@ -2547,7 +2567,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($pull_request_id !== 0 && count($serviceDependencies) > 0) { $serviceDependencies = $serviceDependencies->map(function ($dependency) use ($pull_request_id) { - return $dependency."-pr-$pull_request_id"; + return $dependency . "-pr-$pull_request_id"; }); data_set($service, 'depends_on', $serviceDependencies->toArray()); } @@ -2720,7 +2740,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($server, $containerName); } else { - $fqdn = generateFqdn($server, Str::lower($forService).'-'.$resource->uuid); + $fqdn = generateFqdn($server, Str::lower($forService) . '-' . $resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -2924,7 +2944,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal }); if ($pull_request_id !== 0) { $services->each(function ($service, $serviceName) use ($pull_request_id, $services) { - $services[$serviceName."-pr-$pull_request_id"] = $service; + $services[$serviceName . "-pr-$pull_request_id"] = $service; data_forget($services, $serviceName); }); } @@ -3352,22 +3372,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if ($type->value() === 'bind') { if ($source->value() === '/var/run/docker.sock') { - $volume = $source->value().':'.$target->value(); + $volume = $source->value() . ':' . $target->value(); } elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') { - $volume = $source->value().':'.$target->value(); + $volume = $source->value() . ':' . $target->value(); } else { if ((int) $resource->compose_parsing_version >= 4) { if ($isApplication) { - $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid); + $mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid); } elseif ($isService) { - $mainDirectory = str(base_configuration_dir().'/services/'.$uuid); + $mainDirectory = str(base_configuration_dir() . '/services/' . $uuid); } } else { - $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid); + $mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid); } $source = replaceLocalSource($source, $mainDirectory); if ($isApplication && $isPullRequest) { - $source = $source."-pr-$pullRequestId"; + $source = $source . "-pr-$pullRequestId"; } LocalFileVolume::updateOrCreate( [ @@ -3387,12 +3407,12 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int if (isDev()) { if ((int) $resource->compose_parsing_version >= 4) { if ($isApplication) { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid); } elseif ($isService) { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/' . $uuid); } } else { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid); } } $volume = "$source:$target"; @@ -4061,7 +4081,7 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla $limiterKey, $maxAttempts = 0, function () use (&$rateLimited, &$limiterKey, $callbackOnSuccess) { - isDev() && loggy('Rate limit not reached for '.$limiterKey); + isDev() && loggy('Rate limit not reached for ' . $limiterKey); $rateLimited = false; if ($callbackOnSuccess) { @@ -4071,7 +4091,7 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla $decaySeconds, ); if (! $executed) { - isDev() && loggy('Rate limit reached for '.$limiterKey.'. Rate limiter will be disabled for '.$decaySeconds.' seconds.'); + isDev() && loggy('Rate limit reached for ' . $limiterKey . '. Rate limiter will be disabled for ' . $decaySeconds . ' seconds.'); $rateLimited = true; } From 319c3023dcf4e47f1d7919287b3a91393af45178 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 2 Dec 2024 22:49:55 +0100 Subject: [PATCH 45/75] fix --- app/Actions/Docker/GetContainersStatus.php | 8 +- app/Actions/Server/ServerCheck.php | 4 +- .../Controllers/Api/ServicesController.php | 4 +- app/Jobs/PushServerUpdateJob.php | 2 +- app/Models/BaseModel.php | 6 +- app/Models/StandalonePostgresql.php | 16 +-- app/Models/Team.php | 2 +- bootstrap/helpers/shared.php | 136 +++++++++--------- 8 files changed, 90 insertions(+), 88 deletions(-) diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index e9f577e06..706356930 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -260,7 +260,7 @@ class GetContainersStatus $environmentName = data_get($service, 'environment.name'); if ($projectUuid && $serviceUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/service/' . $serviceUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid; } else { $url = null; } @@ -286,7 +286,7 @@ class GetContainersStatus $environment = data_get($application, 'environment.name'); if ($projectUuid && $applicationUuid && $environment) { - $url = base_url() . '/project/' . $projectUuid . '/' . $environment . '/application/' . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid; } else { $url = null; } @@ -311,7 +311,7 @@ class GetContainersStatus $applicationUuid = data_get($preview, 'application.uuid'); if ($projectUuid && $applicationUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/application/' . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid; } else { $url = null; } @@ -336,7 +336,7 @@ class GetContainersStatus $databaseUuid = data_get($database, 'uuid'); if ($projectUuid && $databaseUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . '/' . $environmentName . '/database/' . $databaseUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid; } else { $url = null; } diff --git a/app/Actions/Server/ServerCheck.php b/app/Actions/Server/ServerCheck.php index 35b4996b4..75b8501f3 100644 --- a/app/Actions/Server/ServerCheck.php +++ b/app/Actions/Server/ServerCheck.php @@ -213,7 +213,7 @@ class ServerCheck if ($isPublic) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { if ($this->isSentinel) { - return data_get($value, 'name') === $uuid . '-proxy'; + return data_get($value, 'name') === $uuid.'-proxy'; } else { if ($this->server->isSwarm()) { @@ -245,7 +245,7 @@ class ServerCheck if ($isPublic) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { if ($this->isSentinel) { - return data_get($value, 'name') === $uuid . '-proxy'; + return data_get($value, 'name') === $uuid.'-proxy'; } else { if ($this->server->isSwarm()) { return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index abf8a24af..bf90322e2 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -308,7 +308,7 @@ class ServicesController extends Controller } if ($oneClickService) { $service_payload = [ - 'name' => "$oneClickServiceName-" . str()->random(10), + 'name' => "$oneClickServiceName-".str()->random(10), 'docker_compose_raw' => base64_decode($oneClickService), 'environment_id' => $environment->id, 'service_type' => $oneClickServiceName, @@ -320,7 +320,7 @@ class ServicesController extends Controller data_set($service_payload, 'connect_to_docker_network', true); } $service = Service::create($service_payload); - $service->name = "$oneClickServiceName-" . $service->uuid; + $service->name = "$oneClickServiceName-".$service->uuid; $service->save(); if ($oneClickDotEnvs?->count() > 0) { $oneClickDotEnvs->each(function ($value) use ($service) { diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index fa146ff96..e9fcb2f37 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -280,7 +280,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue })->first(); if (! $tcpProxyContainerFound) { StartDatabaseProxy::dispatch($database); - $this->server->team?->notify(new ContainerRestarted("TCP Proxy for database", $this->server)); + $this->server->team?->notify(new ContainerRestarted('TCP Proxy for database', $this->server)); } else { } } diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 500c34fb6..79801987b 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -19,16 +19,18 @@ abstract class BaseModel extends Model } }); } + public function name(): Attribute { return new Attribute( - get: fn() => sanitize_string($this->getRawOriginal('name')), + get: fn () => sanitize_string($this->getRawOriginal('name')), ); } + public function image(): Attribute { return new Attribute( - get: fn() => sanitize_string($this->getRawOriginal('image')), + get: fn () => sanitize_string($this->getRawOriginal('image')), ); } } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 99cc8a55b..4a457a6cf 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -25,7 +25,7 @@ class StandalonePostgresql extends BaseModel { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'postgres-data-' . $database->uuid, + 'name' => 'postgres-data-'.$database->uuid, 'mount_path' => '/var/lib/postgresql/data', 'host_path' => null, 'resource_id' => $database->id, @@ -48,7 +48,7 @@ class StandalonePostgresql extends BaseModel public function workdir() { - return database_configuration_dir() . "/{$this->uuid}"; + return database_configuration_dir()."/{$this->uuid}"; } protected function serverStatus(): Attribute @@ -65,7 +65,7 @@ class StandalonePostgresql extends BaseModel $server = data_get($this, 'destination.server'); $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { - instant_remote_process(['rm -rf ' . $this->workdir()], $server, false); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } @@ -82,7 +82,7 @@ class StandalonePostgresql extends BaseModel public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->postgres_initdb_args . $this->postgres_host_auth_method; + $newConfigHash = $this->image.$this->ports_mappings.$this->postgres_initdb_args.$this->postgres_host_auth_method; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -186,14 +186,14 @@ class StandalonePostgresql extends BaseModel public function portsMappings(): Attribute { return Attribute::make( - set: fn($value) => $value === '' ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } public function portsMappingsArray(): Attribute { return Attribute::make( - get: fn() => is_null($this->ports_mappings) + get: fn () => is_null($this->ports_mappings) ? [] : explode(',', $this->ports_mappings), @@ -208,7 +208,7 @@ class StandalonePostgresql extends BaseModel public function databaseType(): Attribute { return new Attribute( - get: fn() => $this->type(), + get: fn () => $this->type(), ); } @@ -220,7 +220,7 @@ class StandalonePostgresql extends BaseModel protected function internalDbUrl(): Attribute { return new Attribute( - get: fn() => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}", + get: fn () => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}", ); } diff --git a/app/Models/Team.php b/app/Models/Team.php index 76d89fb8e..6ba044349 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -130,7 +130,7 @@ class Team extends Model implements SendsDiscord, SendsEmail public function name(): Attribute { return new Attribute( - get: fn() => sanitize_string($this->getRawOriginal('name')), + get: fn () => sanitize_string($this->getRawOriginal('name')), ); } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index ddc578400..d64b5ab6e 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -67,27 +67,27 @@ function base_configuration_dir(): string } function application_configuration_dir(): string { - return base_configuration_dir() . '/applications'; + return base_configuration_dir().'/applications'; } function service_configuration_dir(): string { - return base_configuration_dir() . '/services'; + return base_configuration_dir().'/services'; } function database_configuration_dir(): string { - return base_configuration_dir() . '/databases'; + return base_configuration_dir().'/databases'; } function database_proxy_dir($uuid): string { - return base_configuration_dir() . "/databases/$uuid/proxy"; + return base_configuration_dir()."/databases/$uuid/proxy"; } function backup_dir(): string { - return base_configuration_dir() . '/backups'; + return base_configuration_dir().'/backups'; } function metrics_dir(): string { - return base_configuration_dir() . '/metrics'; + return base_configuration_dir().'/metrics'; } function sanitize_string(string $input): string @@ -142,8 +142,8 @@ function refreshSession(?Team $team = null): void $team = User::find(Auth::id())->teams->first(); } } - Cache::forget('team:' . Auth::id()); - Cache::remember('team:' . Auth::id(), 3600, function () use ($team) { + Cache::forget('team:'.Auth::id()); + Cache::remember('team:'.Auth::id(), 3600, function () use ($team) { return $team; }); session(['currentTeam' => $team]); @@ -175,7 +175,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n $message = null; } if ($customErrorMessage) { - $message = $customErrorMessage . ' ' . $message; + $message = $customErrorMessage.' '.$message; } if (isset($livewire)) { @@ -430,7 +430,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null Mail::send( [], [], - fn(Message $message) => $message + fn (Message $message) => $message ->to($email) ->replyTo($email) ->cc($cc) @@ -441,7 +441,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null Mail::send( [], [], - fn(Message $message) => $message + fn (Message $message) => $message ->to($email) ->subject($mail->subject) ->html((string) $mail->render()) @@ -677,19 +677,19 @@ function queryResourcesByUuid(string $uuid) function generateTagDeployWebhook($tag_name) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl) . '/api/v1'; + $api = Url::fromString($baseUrl).'/api/v1'; $endpoint = "/deploy?tag=$tag_name"; - return $api . $endpoint; + return $api.$endpoint; } function generateDeployWebhook($resource) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl) . '/api/v1'; + $api = Url::fromString($baseUrl).'/api/v1'; $endpoint = '/deploy'; $uuid = data_get($resource, 'uuid'); - return $api . $endpoint . "?uuid=$uuid&force=false"; + return $api.$endpoint."?uuid=$uuid&force=false"; } function generateGitManualWebhook($resource, $type) { @@ -699,7 +699,7 @@ function generateGitManualWebhook($resource, $type) if ($resource->getMorphClass() === \App\Models\Application::class) { $baseUrl = base_url(); - return Url::fromString($baseUrl) . "/webhooks/source/$type/events/manual"; + return Url::fromString($baseUrl)."/webhooks/source/$type/events/manual"; } return null; @@ -1067,7 +1067,7 @@ function validate_dns_entry(string $fqdn, Server $server) $query = new DNSQuery($dns_server); $results = $query->query($host, $type); if ($results === false || $query->hasError()) { - ray('Error: ' . $query->getLasterror()); + ray('Error: '.$query->getLasterror()); } else { foreach ($results as $result) { if ($result->getType() == $type) { @@ -1077,7 +1077,7 @@ function validate_dns_entry(string $fqdn, Server $server) break; } if ($result->getData() === $ip) { - ray($host . ' has IP address ' . $result->getData()); + ray($host.' has IP address '.$result->getData()); ray($result->getString()); $found_matching_ip = true; break; @@ -1125,15 +1125,15 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = $applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']); $serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid']); if ($uuid) { - $applications = $applications->filter(fn($app) => $app->uuid !== $uuid); - $serviceApplications = $serviceApplications->filter(fn($app) => $app->uuid !== $uuid); + $applications = $applications->filter(fn ($app) => $app->uuid !== $uuid); + $serviceApplications = $serviceApplications->filter(fn ($app) => $app->uuid !== $uuid); } $domainFound = false; foreach ($applications as $app) { if (is_null($app->fqdn)) { continue; } - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1152,7 +1152,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = if (str($app->fqdn)->isEmpty()) { continue; } - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1202,7 +1202,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null }); $apps = Application::all(); foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1221,7 +1221,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null } $apps = ServiceApplication::all(); foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn($fqdn) => $fqdn !== ''); + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); @@ -1278,7 +1278,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array $commands = $commands->map(function ($line) use ($server) { if (Str::startsWith($line, 'sudo mkdir -p')) { - return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p'); + return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p'); } return $line; @@ -1310,7 +1310,7 @@ function parseLineForSudo(string $command, Server $server): string $command = "sudo $command"; } if (Str::startsWith($command, 'sudo mkdir -p')) { - $command = "$command && sudo chown -R $server->user:$server->user " . Str::after($command, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($command, 'sudo mkdir -p'); + $command = "$command && sudo chown -R $server->user:$server->user ".Str::after($command, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($command, 'sudo mkdir -p'); } if (str($command)->contains('$(') || str($command)->contains('`')) { $command = str($command)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value(); @@ -1447,9 +1447,9 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull return $volume; } if (get_class($resource) === \App\Models\Application::class) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; } else { - $dir = base_configuration_dir() . '/services/' . $resource->service->uuid; + $dir = base_configuration_dir().'/services/'.$resource->service->uuid; } if ($source->startsWith('.')) { @@ -1459,7 +1459,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull $source = $source->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $source = $source . "-pr-$pull_request_id"; + $source = $source."-pr-$pull_request_id"; } if (! $resource?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git) { LocalFileVolume::updateOrCreate( @@ -1917,7 +1917,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (! $isDatabase) { if ($savedService->fqdn) { - data_set($savedService, 'fqdn', $savedService->fqdn . ',' . $fqdn); + data_set($savedService, 'fqdn', $savedService->fqdn.','.$fqdn); } else { data_set($savedService, 'fqdn', $fqdn); } @@ -1977,7 +1977,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($resource->server, $containerName); } else { - $fqdn = generateFqdn($resource->server, Str::lower($forService) . '-' . $resource->uuid); + $fqdn = generateFqdn($resource->server, Str::lower($forService).'-'.$resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -2319,7 +2319,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if ($name->startsWith('.')) { $name = $name->replaceFirst('.', $dir); } @@ -2327,12 +2327,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $name->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } else { if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; $volume = str("$name:$mount"); if ($topLevelVolumes->has($name)) { $v = $topLevelVolumes->get($name); @@ -2371,7 +2371,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } @@ -2382,7 +2382,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $read_only = data_get($volume, 'read_only'); if ($source && $target) { if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if (str($source, '.')) { $source = str($source)->replaceFirst('.', $dir); } @@ -2390,21 +2390,21 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $source = str($source)->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $source = $source . "-pr-$pull_request_id"; + $source = $source."-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } } else { if ($pull_request_id !== 0) { - $source = $source . "-pr-$pull_request_id"; + $source = $source."-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } if (! str($source)->startsWith('/')) { if ($topLevelVolumes->has($source)) { @@ -2443,7 +2443,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if ($name->startsWith('.')) { $name = $name->replaceFirst('.', $dir); } @@ -2451,13 +2451,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $name->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } else { if ($pull_request_id !== 0) { $uuid = $resource->uuid; - $name = $uuid . "-$name-pr-$pull_request_id"; + $name = $uuid."-$name-pr-$pull_request_id"; $volume = str("$name:$mount"); if ($topLevelVolumes->has($name)) { $v = $topLevelVolumes->get($name); @@ -2476,7 +2476,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } else { $uuid = $resource->uuid; - $name = str($uuid . "-$name"); + $name = str($uuid."-$name"); $volume = str("$name:$mount"); if ($topLevelVolumes->has($name->value())) { $v = $topLevelVolumes->get($name->value()); @@ -2499,7 +2499,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } @@ -2511,7 +2511,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($source && $target) { $uuid = $resource->uuid; if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if (str($source, '.')) { $source = str($source)->replaceFirst('.', $dir); } @@ -2519,20 +2519,20 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $source = str($source)->replaceFirst('~', $dir); } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } } else { if ($pull_request_id === 0) { - $source = $uuid . "-$source"; + $source = $uuid."-$source"; } else { - $source = $uuid . "-$source-pr-$pull_request_id"; + $source = $uuid."-$source-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } if (! str($source)->startsWith('/')) { if ($topLevelVolumes->has($source)) { @@ -2567,7 +2567,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($pull_request_id !== 0 && count($serviceDependencies) > 0) { $serviceDependencies = $serviceDependencies->map(function ($dependency) use ($pull_request_id) { - return $dependency . "-pr-$pull_request_id"; + return $dependency."-pr-$pull_request_id"; }); data_set($service, 'depends_on', $serviceDependencies->toArray()); } @@ -2740,7 +2740,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($server, $containerName); } else { - $fqdn = generateFqdn($server, Str::lower($forService) . '-' . $resource->uuid); + $fqdn = generateFqdn($server, Str::lower($forService).'-'.$resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -2944,7 +2944,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal }); if ($pull_request_id !== 0) { $services->each(function ($service, $serviceName) use ($pull_request_id, $services) { - $services[$serviceName . "-pr-$pull_request_id"] = $service; + $services[$serviceName."-pr-$pull_request_id"] = $service; data_forget($services, $serviceName); }); } @@ -3372,22 +3372,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if ($type->value() === 'bind') { if ($source->value() === '/var/run/docker.sock') { - $volume = $source->value() . ':' . $target->value(); + $volume = $source->value().':'.$target->value(); } elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') { - $volume = $source->value() . ':' . $target->value(); + $volume = $source->value().':'.$target->value(); } else { if ((int) $resource->compose_parsing_version >= 4) { if ($isApplication) { - $mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid); + $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid); } elseif ($isService) { - $mainDirectory = str(base_configuration_dir() . '/services/' . $uuid); + $mainDirectory = str(base_configuration_dir().'/services/'.$uuid); } } else { - $mainDirectory = str(base_configuration_dir() . '/applications/' . $uuid); + $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid); } $source = replaceLocalSource($source, $mainDirectory); if ($isApplication && $isPullRequest) { - $source = $source . "-pr-$pullRequestId"; + $source = $source."-pr-$pullRequestId"; } LocalFileVolume::updateOrCreate( [ @@ -3407,12 +3407,12 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int if (isDev()) { if ((int) $resource->compose_parsing_version >= 4) { if ($isApplication) { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid); } elseif ($isService) { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/' . $uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid); } } else { - $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/' . $uuid); + $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid); } } $volume = "$source:$target"; @@ -4081,7 +4081,7 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla $limiterKey, $maxAttempts = 0, function () use (&$rateLimited, &$limiterKey, $callbackOnSuccess) { - isDev() && loggy('Rate limit not reached for ' . $limiterKey); + isDev() && loggy('Rate limit not reached for '.$limiterKey); $rateLimited = false; if ($callbackOnSuccess) { @@ -4091,7 +4091,7 @@ function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?calla $decaySeconds, ); if (! $executed) { - isDev() && loggy('Rate limit reached for ' . $limiterKey . '. Rate limiter will be disabled for ' . $decaySeconds . ' seconds.'); + isDev() && loggy('Rate limit reached for '.$limiterKey.'. Rate limiter will be disabled for '.$decaySeconds.' seconds.'); $rateLimited = true; } From f7ef339ec3f6e5568dbad3fb7220affea58a3e90 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 12:21:09 +0100 Subject: [PATCH 46/75] Improve notification message for TCP Proxy container restart to include database name --- app/Jobs/PushServerUpdateJob.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index e9fcb2f37..24f8d1e6b 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -280,7 +280,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue })->first(); if (! $tcpProxyContainerFound) { StartDatabaseProxy::dispatch($database); - $this->server->team?->notify(new ContainerRestarted('TCP Proxy for database', $this->server)); + $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); } else { } } From 669293afc1b3f7190a43b279fb62a5118bb9039d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 12:22:27 +0100 Subject: [PATCH 47/75] disable success notifications for now --- app/Jobs/ApplicationDeploymentJob.php | 2 +- app/Jobs/DatabaseBackupJob.php | 2 +- app/Models/Server.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 6a66bb56d..41909fa30 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -2400,7 +2400,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); if (! $this->only_this_server) { $this->deploy_to_additional_destinations(); } - $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); + //$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); } } diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 89674b255..ee702202f 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -306,7 +306,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue if ($this->backup->save_s3) { $this->upload_to_s3(); } - $this->team?->notify(new BackupSuccess($this->backup, $this->database, $database)); + //$this->team?->notify(new BackupSuccess($this->backup, $this->database, $database)); $this->backup_log->update([ 'status' => 'success', 'message' => $this->backup_output, diff --git a/app/Models/Server.php b/app/Models/Server.php index 27c2b9b99..83b91b254 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -1039,7 +1039,7 @@ $schema://$host { $this->unreachable_notification_sent = false; $this->save(); $this->refresh(); - $this->team->notify(new Reachable($this)); + // $this->team->notify(new Reachable($this)); } public function sendUnreachableNotification() @@ -1047,7 +1047,7 @@ $schema://$host { $this->unreachable_notification_sent = true; $this->save(); $this->refresh(); - $this->team->notify(new Unreachable($this)); + // $this->team->notify(new Unreachable($this)); } public function validateConnection(bool $justCheckingNewKey = false) From c74728162ede3aa137affd457bdc606d0d69be77 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:41:56 +0100 Subject: [PATCH 48/75] wip: test rename GitHub app --- app/Livewire/Source/Github/Change.php | 5 +++++ .../views/livewire/source/github/change.blade.php | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 07cef54f9..79cc02219 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -142,6 +142,11 @@ class Change extends Component } } + public function getUpdatePath() + { + return "{$this->github_app->html_url}/settings/apps/{$this->github_app->app_id}"; + } + public function submit() { try { diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php index 5e576fa85..215c54411 100644 --- a/resources/views/livewire/source/github/change.blade.php +++ b/resources/views/livewire/source/github/change.blade.php @@ -58,7 +58,15 @@ @else
From f38196c4213e1358d89c35c66f4c3b4f8ba6e258 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:54:20 +0100 Subject: [PATCH 49/75] fix: URL and sync new app name --- app/Livewire/Source/Github/Change.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 79cc02219..5d57ecf1e 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -4,6 +4,7 @@ namespace App\Livewire\Source\Github; use App\Jobs\GithubAppPermissionJob; use App\Models\GithubApp; +use Illuminate\Support\Facades\Http; use Livewire\Component; class Change extends Component @@ -144,7 +145,29 @@ class Change extends Component public function getUpdatePath() { - return "{$this->github_app->html_url}/settings/apps/{$this->github_app->app_id}"; + return "{$this->github_app->html_url}/settings/apps/{$this->github_app->name}"; + } + + public function syncGithubAppName() + { + try { + $github_access_token = generate_github_installation_token($this->github_app); + + $response = Http::withToken($github_access_token) + ->get("{$this->github_app->api_url}/app"); + + if ($response->successful()) { + $app_data = $response->json(); + if ($app_data['name'] !== $this->github_app->name) { + $this->github_app->name = $app_data['name']; + $this->github_app->save(); + $this->name = str($this->github_app->name)->kebab(); + $this->dispatch('success', 'Github App name synchronized.'); + } + } + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function submit() From 737e81aa38f854b860047e3782912e33cab244a3 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:12:58 +0100 Subject: [PATCH 50/75] wip button to sync new app name --- app/Livewire/Source/Github/Change.php | 20 ++++++++++++++----- .../livewire/source/github/change.blade.php | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 5d57ecf1e..55ced1e3e 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -154,16 +154,26 @@ class Change extends Component $github_access_token = generate_github_installation_token($this->github_app); $response = Http::withToken($github_access_token) - ->get("{$this->github_app->api_url}/app"); + ->withHeaders([ + 'Accept' => 'application/vnd.github+json', + 'X-GitHub-Api-Version' => '2022-11-28', + ]) + ->get("{$this->github_app->api_url}/installation/repositories"); if ($response->successful()) { $app_data = $response->json(); - if ($app_data['name'] !== $this->github_app->name) { - $this->github_app->name = $app_data['name']; + $app_name = data_get($app_data, 'installation.app_slug'); + + if ($app_name && $app_name !== $this->github_app->name) { + $this->github_app->name = $app_name; + $this->name = str($app_name)->kebab(); $this->github_app->save(); - $this->name = str($this->github_app->name)->kebab(); - $this->dispatch('success', 'Github App name synchronized.'); + $this->dispatch('success', 'Github App name synchronized successfully.'); + } else { + $this->dispatch('info', 'Github App name is already up to date.'); } + } else { + $this->dispatch('error', 'Failed to fetch Github App information. Status: '.$response->status()); } } catch (\Throwable $e) { return handleError($e, $this); diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php index 215c54411..2d873b465 100644 --- a/resources/views/livewire/source/github/change.blade.php +++ b/resources/views/livewire/source/github/change.blade.php @@ -60,6 +60,7 @@
+ Sync Name Update From 7b0a902f25d3aad7b1657c7004bfb294c75e9f05 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 14:02:01 +0100 Subject: [PATCH 51/75] version++ --- config/constants.php | 2 +- versions.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/constants.php b/config/constants.php index c947635be..4923cfc15 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.374', + 'version' => '4.0.0-beta.375', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), diff --git a/versions.json b/versions.json index 8175f7bae..1c4e813ea 100644 --- a/versions.json +++ b/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.374" + "version": "4.0.0-beta.375" }, "nightly": { - "version": "4.0.0-beta.375" + "version": "4.0.0-beta.376" }, "helper": { "version": "1.0.4" From 869eaedaf57d04d17a71f91b3a53f10e9a36f423 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 14:02:12 +0100 Subject: [PATCH 52/75] revert query logic --- .../Project/Application/Configuration.php | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/Livewire/Project/Application/Configuration.php b/app/Livewire/Project/Application/Configuration.php index cce3bdd39..5261a0800 100644 --- a/app/Livewire/Project/Application/Configuration.php +++ b/app/Livewire/Project/Application/Configuration.php @@ -16,24 +16,26 @@ class Configuration extends Component public function mount() { - $this->application = Application::query() - ->whereHas('environment.project', function ($query) { - $query->where('team_id', currentTeam()->id) - ->where('uuid', request()->route('project_uuid')); - }) - ->whereHas('environment', function ($query) { - $query->where('name', request()->route('environment_name')); - }) + $project = currentTeam() + ->projects() + ->select('id', 'uuid', 'team_id') + ->where('uuid', request()->route('project_uuid')) + ->firstOrFail(); + $environment = $project->environments() + ->select('id', 'name', 'project_id') + ->where('name', request()->route('environment_name')) + ->firstOrFail(); + $application = $environment->applications() + ->with(['destination']) ->where('uuid', request()->route('application_uuid')) - ->with(['destination' => function ($query) { - $query->select('id', 'server_id'); - }]) ->firstOrFail(); - if ($this->application->destination && $this->application->destination->server_id) { + $this->application = $application; + if ($application->destination && $application->destination->server) { + $mainServer = $application->destination->server; $this->servers = Server::ownedByCurrentTeam() ->select('id', 'name') - ->where('id', '!=', $this->application->destination->server_id) + ->where('id', '!=', $mainServer->id) ->get(); } else { $this->servers = collect(); From ec9315f24ec8fb9c24eb01f373d3f263d42bd025 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 14:09:51 +0100 Subject: [PATCH 53/75] remove unnecessary things --- app/Actions/Database/StartClickhouse.php | 2 +- app/Actions/Database/StartDragonfly.php | 2 +- app/Actions/Database/StartKeydb.php | 2 +- app/Actions/Database/StartMariadb.php | 2 +- app/Actions/Database/StartMongodb.php | 2 +- app/Actions/Database/StartMysql.php | 2 +- app/Actions/Database/StartPostgresql.php | 2 +- app/Actions/Database/StartRedis.php | 2 +- app/Console/Commands/CloudCleanupSubscriptions.php | 6 +++--- app/Models/Team.php | 7 ------- resources/views/livewire/switch-team.blade.php | 2 +- 11 files changed, 12 insertions(+), 19 deletions(-) diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php index 13667e829..42c6e1449 100644 --- a/app/Actions/Database/StartClickhouse.php +++ b/app/Actions/Database/StartClickhouse.php @@ -24,7 +24,7 @@ class StartClickhouse $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php index c72714e1c..ea235be4e 100644 --- a/app/Actions/Database/StartDragonfly.php +++ b/app/Actions/Database/StartDragonfly.php @@ -26,7 +26,7 @@ class StartDragonfly $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index bd98258ab..010bf5884 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -27,7 +27,7 @@ class StartKeydb $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index 696dd7ff4..2437a013e 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -24,7 +24,7 @@ class StartMariadb $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 26a0f82d0..a33e72c27 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -30,7 +30,7 @@ class StartMongodb } $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index a3694648f..0b19b3f0c 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -24,7 +24,7 @@ class StartMysql $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index f5e85087f..7faa232c3 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -25,7 +25,7 @@ class StartPostgresql $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/", ]; diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index 7a2d2b34d..bacf49f82 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -25,7 +25,7 @@ class StartRedis $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ - "echo 'Starting {$database->name}.'", + "echo 'Starting database.'", "mkdir -p $this->configuration_dir", ]; diff --git a/app/Console/Commands/CloudCleanupSubscriptions.php b/app/Console/Commands/CloudCleanupSubscriptions.php index 8bb420ab8..9198b003e 100644 --- a/app/Console/Commands/CloudCleanupSubscriptions.php +++ b/app/Console/Commands/CloudCleanupSubscriptions.php @@ -36,7 +36,7 @@ class CloudCleanupSubscriptions extends Command } // If the team has no subscription id and the invoice is paid, we need to reset the invoice paid status if (! (data_get($team, 'subscription.stripe_subscription_id'))) { - $this->info("Resetting invoice paid status for team {$team->id} {$team->name}"); + $this->info("Resetting invoice paid status for team {$team->id}"); $team->subscription->update([ 'stripe_invoice_paid' => false, @@ -61,9 +61,9 @@ class CloudCleanupSubscriptions extends Command $this->info('Subscription id: '.data_get($team, 'subscription.stripe_subscription_id')); $confirm = $this->confirm('Do you want to cancel the subscription?', true); if (! $confirm) { - $this->info("Skipping team {$team->id} {$team->name}"); + $this->info("Skipping team {$team->id}"); } else { - $this->info("Cancelling subscription for team {$team->id} {$team->name}"); + $this->info("Cancelling subscription for team {$team->id}"); $team->subscription->update([ 'stripe_invoice_paid' => false, 'stripe_trial_already_ended' => false, diff --git a/app/Models/Team.php b/app/Models/Team.php index 6ba044349..e21aa3a25 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -127,13 +127,6 @@ class Team extends Model implements SendsDiscord, SendsEmail ]; } - public function name(): Attribute - { - return new Attribute( - get: fn () => sanitize_string($this->getRawOriginal('name')), - ); - } - public function getRecepients($notification) { $recipients = data_get($notification, 'emails', null); diff --git a/resources/views/livewire/switch-team.blade.php b/resources/views/livewire/switch-team.blade.php index 024346147..52500087e 100644 --- a/resources/views/livewire/switch-team.blade.php +++ b/resources/views/livewire/switch-team.blade.php @@ -1,6 +1,6 @@ @foreach (auth()->user()->teams as $team) - + @endforeach From a2860971a6dc8b17072b18962552ff604c28d552 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:36:30 +0100 Subject: [PATCH 54/75] try jwt --- app/Livewire/Source/Github/Change.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 55ced1e3e..4c41cb9e4 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -151,18 +151,18 @@ class Change extends Component public function syncGithubAppName() { try { - $github_access_token = generate_github_installation_token($this->github_app); + $jwt = $this->github_app->generateJWT(); - $response = Http::withToken($github_access_token) + $response = Http::withToken($jwt) ->withHeaders([ 'Accept' => 'application/vnd.github+json', 'X-GitHub-Api-Version' => '2022-11-28', ]) - ->get("{$this->github_app->api_url}/installation/repositories"); + ->get('https://api.github.com/app'); if ($response->successful()) { $app_data = $response->json(); - $app_name = data_get($app_data, 'installation.app_slug'); + $app_name = $app_data['name'] ?? null; if ($app_name && $app_name !== $this->github_app->name) { $this->github_app->name = $app_name; @@ -170,7 +170,7 @@ class Change extends Component $this->github_app->save(); $this->dispatch('success', 'Github App name synchronized successfully.'); } else { - $this->dispatch('info', 'Github App name is already up to date.'); + $this->dispatch('info', 'If you changed the name in GitHub, please wait a few moments and try syncing again.'); } } else { $this->dispatch('error', 'Failed to fetch Github App information. Status: '.$response->status()); From 6d43bbc6b95c552bf3398517a4b79c9b06a48642 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:43:11 +0100 Subject: [PATCH 55/75] fix naming --- app/Livewire/Source/Github/Change.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 4c41cb9e4..4f76d7299 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -151,14 +151,14 @@ class Change extends Component public function syncGithubAppName() { try { - $jwt = $this->github_app->generateJWT(); + $github_access_token = generate_github_installation_token($this->github_app); - $response = Http::withToken($jwt) + $response = Http::withToken($github_access_token) ->withHeaders([ 'Accept' => 'application/vnd.github+json', 'X-GitHub-Api-Version' => '2022-11-28', ]) - ->get('https://api.github.com/app'); + ->get("{$this->github_app->api_url}/app"); if ($response->successful()) { $app_data = $response->json(); From 56f6bdf7a776b951cdad2920c4e3b07a196ada00 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:11:35 +0100 Subject: [PATCH 56/75] use private key to make a jwt --- app/Livewire/Source/Github/Change.php | 58 +++++++++++++++++++++------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 4f76d7299..4968a549d 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -4,7 +4,11 @@ namespace App\Livewire\Source\Github; use App\Jobs\GithubAppPermissionJob; use App\Models\GithubApp; +use App\Models\PrivateKey; use Illuminate\Support\Facades\Http; +use Lcobucci\JWT\Configuration; +use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Rsa\Sha256; use Livewire\Component; class Change extends Component @@ -148,32 +152,60 @@ class Change extends Component return "{$this->github_app->html_url}/settings/apps/{$this->github_app->name}"; } + private function generateGithubJwt($private_key, $app_id): string + { + $configuration = Configuration::forAsymmetricSigner( + new Sha256, + InMemory::plainText($private_key), + InMemory::plainText($private_key) + ); + + $now = time(); + + return $configuration->builder() + ->issuedBy((string) $app_id) + ->permittedFor('https://api.github.com') + ->identifiedBy((string) $now) + ->issuedAt(new \DateTimeImmutable("@{$now}")) + ->expiresAt(new \DateTimeImmutable('@'.($now + 600))) + ->getToken($configuration->signer(), $configuration->signingKey()) + ->toString(); + } + public function syncGithubAppName() { try { - $github_access_token = generate_github_installation_token($this->github_app); + $privateKey = PrivateKey::find($this->github_app->private_key_id); - $response = Http::withToken($github_access_token) - ->withHeaders([ - 'Accept' => 'application/vnd.github+json', - 'X-GitHub-Api-Version' => '2022-11-28', - ]) - ->get("{$this->github_app->api_url}/app"); + if (! $privateKey) { + $this->dispatch('error', 'Private key not found for this GitHub App.'); + + return; + } + + $jwt = $this->generateGithubJwt($privateKey->private_key, $this->github_app->app_id); + + $response = Http::withHeaders([ + 'Accept' => 'application/vnd.github+json', + 'X-GitHub-Api-Version' => '2022-11-28', + 'Authorization' => "Bearer {$jwt}", + ])->get("{$this->github_app->api_url}/app"); if ($response->successful()) { $app_data = $response->json(); - $app_name = $app_data['name'] ?? null; + $app_slug = $app_data['slug'] ?? null; - if ($app_name && $app_name !== $this->github_app->name) { - $this->github_app->name = $app_name; - $this->name = str($app_name)->kebab(); + if ($app_slug) { + $this->github_app->name = $app_slug; + $this->name = str($app_slug)->kebab(); $this->github_app->save(); $this->dispatch('success', 'Github App name synchronized successfully.'); } else { - $this->dispatch('info', 'If you changed the name in GitHub, please wait a few moments and try syncing again.'); + $this->dispatch('info', 'Could not find app slug in GitHub response.'); } } else { - $this->dispatch('error', 'Failed to fetch Github App information. Status: '.$response->status()); + $error_message = $response->json()['message'] ?? 'Unknown error'; + $this->dispatch('error', "Failed to fetch Github App information: {$error_message}"); } } catch (\Throwable $e) { return handleError($e, $this); From 5f985426abc7d11f8d16fb675f6c3430fc4e6ae1 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:27:20 +0100 Subject: [PATCH 57/75] feat: update private key nam with new slug as well --- app/Http/Controllers/Webhook/Github.php | 2 +- app/Livewire/Source/Github/Change.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php index 3683adaa8..ac1d4ded2 100644 --- a/app/Http/Controllers/Webhook/Github.php +++ b/app/Http/Controllers/Webhook/Github.php @@ -463,7 +463,7 @@ class Github extends Controller $private_key = data_get($data, 'pem'); $webhook_secret = data_get($data, 'webhook_secret'); $private_key = PrivateKey::create([ - 'name' => $slug, + 'name' => "github-app-{$slug}", 'private_key' => $private_key, 'team_id' => $github_app->team_id, 'is_git_related' => true, diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 4968a549d..7f9200891 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -198,8 +198,10 @@ class Change extends Component if ($app_slug) { $this->github_app->name = $app_slug; $this->name = str($app_slug)->kebab(); + $privateKey->name = "github-app-{$app_slug}"; + $privateKey->save(); $this->github_app->save(); - $this->dispatch('success', 'Github App name synchronized successfully.'); + $this->dispatch('success', 'Github App name and SSH key name synchronized successfully.'); } else { $this->dispatch('info', 'Could not find app slug in GitHub response.'); } From 74311f4feb482b370c945923e049e2aa4fe68ee9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 3 Dec 2024 15:39:24 +0100 Subject: [PATCH 58/75] Refactor string concatenation and update function signatures for improved readability and null handling in shared helper functions --- bootstrap/helpers/shared.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index d64b5ab6e..a3ef93dfc 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -90,8 +90,11 @@ function metrics_dir(): string return base_configuration_dir().'/metrics'; } -function sanitize_string(string $input): string +function sanitize_string(?string $input = null): ?string { + if (is_null($input)) { + return null; + } // Remove any HTML/PHP tags $sanitized = strip_tags($input); From fef8d0c62c103df93d840d3e86eab488940915fb Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:50:45 +0100 Subject: [PATCH 59/75] fix: typos and naming --- app/Livewire/Source/Github/Change.php | 14 ++++++++------ .../views/livewire/source/github/change.blade.php | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 7f9200891..b1f0605bb 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -62,6 +62,7 @@ class Change extends Component $this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->dispatch('success', 'Github App permissions updated.'); } + // public function check() // { @@ -95,6 +96,7 @@ class Change extends Component // ray($runners_by_repository); // } + public function mount() { try { @@ -147,7 +149,7 @@ class Change extends Component } } - public function getUpdatePath() + public function getGithubAppNameUpdatePath() { return "{$this->github_app->html_url}/settings/apps/{$this->github_app->name}"; } @@ -172,13 +174,13 @@ class Change extends Component ->toString(); } - public function syncGithubAppName() + public function updateGithubAppName() { try { $privateKey = PrivateKey::find($this->github_app->private_key_id); if (! $privateKey) { - $this->dispatch('error', 'Private key not found for this GitHub App.'); + $this->dispatch('error', 'No private key found for this GitHub App.'); return; } @@ -201,13 +203,13 @@ class Change extends Component $privateKey->name = "github-app-{$app_slug}"; $privateKey->save(); $this->github_app->save(); - $this->dispatch('success', 'Github App name and SSH key name synchronized successfully.'); + $this->dispatch('success', 'GitHub App name and SSH key name synchronized successfully.'); } else { - $this->dispatch('info', 'Could not find app slug in GitHub response.'); + $this->dispatch('info', 'Could not find App Name (slug) in GitHub response.'); } } else { $error_message = $response->json()['message'] ?? 'Unknown error'; - $this->dispatch('error', "Failed to fetch Github App information: {$error_message}"); + $this->dispatch('error', "Failed to fetch GitHub App information: {$error_message}"); } } catch (\Throwable $e) { return handleError($e, $this); diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php index 2d873b465..3c11646c2 100644 --- a/resources/views/livewire/source/github/change.blade.php +++ b/resources/views/livewire/source/github/change.blade.php @@ -60,10 +60,12 @@
- Sync Name - + + Sync Name + + - Update + Rename From f51300a19213df0d29fbf0161fc91495bd296c52 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:17:35 +0100 Subject: [PATCH 60/75] fix: client and webhook secret disappear after sync --- app/Livewire/Source/Github/Change.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index b1f0605bb..51c5ae3e9 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -56,6 +56,13 @@ class Change extends Component 'github_app.administration' => 'nullable|string', ]; + public function boot() + { + if ($this->github_app) { + $this->github_app->makeVisible(['client_secret', 'webhook_secret']); + } + } + public function checkPermissions() { GithubAppPermissionJob::dispatchSync($this->github_app); @@ -102,10 +109,10 @@ class Change extends Component try { $github_app_uuid = request()->github_app_uuid; $this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail(); + $this->github_app->makeVisible(['client_secret', 'webhook_secret']); $this->applications = $this->github_app->applications; $settings = instanceSettings(); - $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->name = str($this->github_app->name)->kebab(); $this->fqdn = $settings->fqdn; From 21f8d8b492d996d985e8b15adf552600cc2ee743 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:54:29 +0100 Subject: [PATCH 61/75] Update service-templates.json --- templates/service-templates.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/templates/service-templates.json b/templates/service-templates.json index 222ca1601..1532c1609 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -2170,6 +2170,21 @@ "logo": "svgs/plane.svg", "minversion": "0.0.0" }, + "plex": { + "documentation": "https://docs.linuxserver.io/images/docker-plex/?utm_source=coolify.io", + "slogan": "Plex organizes video, music and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices.", + "compose": "c2VydmljZXM6CiAgcGxleDoKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9wbGV4OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9QTEVYXzMyNDAwCiAgICAgIC0gX0FQUF9VUkw9JFNFUlZJQ0VfRlFETl9QTEVYCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gJ1RaPSR7VFo6LUFtZXJpY2EvVG9yb250b30nCiAgICAgIC0gJ1BMRVhfQ0xBSU09JHtQTEVYX0NMQUlNfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BsZXgtY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ3BsZXgtdHY6L3R2JwogICAgICAtICdwbGV4LW1vdmllczovbW92aWVzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMyNDAwL2lkZW50aXR5JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", + "tags": [ + "media", + "server", + "movies", + "tv", + "music" + ], + "logo": "svgs/plex.svg", + "minversion": "0.0.0", + "port": "32400" + }, "plunk": { "documentation": "https://docs.useplunk.com/getting-started/introduction?utm_source=coolify.io", "slogan": "Plunk, The Open-Source Email Platform for AWS", From 26ba433fd3f4371fd971f57cfb41b001a364fa45 Mon Sep 17 00:00:00 2001 From: Jeremy Angele Date: Tue, 3 Dec 2024 22:24:36 +0100 Subject: [PATCH 62/75] Use computed property for timezones --- app/Livewire/Server/Show.php | 15 ++++++++++----- resources/views/livewire/server/show.blade.php | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Livewire/Server/Show.php b/app/Livewire/Server/Show.php index a5544489d..ac5211c1b 100644 --- a/app/Livewire/Server/Show.php +++ b/app/Livewire/Server/Show.php @@ -5,7 +5,7 @@ namespace App\Livewire\Server; use App\Actions\Server\StartSentinel; use App\Actions\Server\StopSentinel; use App\Models\Server; -use Livewire\Attributes\Locked; +use Livewire\Attributes\Computed; use Livewire\Attributes\Validate; use Livewire\Component; @@ -79,9 +79,6 @@ class Show extends Component #[Validate(['required'])] public string $serverTimezone; - #[Locked] - public array $timezones; - public function getListeners() { $teamId = auth()->user()->currentTeam()->id; @@ -96,13 +93,21 @@ class Show extends Component { try { $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); - $this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray(); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); } } + #[Computed] + public function timezones(): array + { + return collect(timezone_identifiers_list()) + ->sort() + ->values() + ->toArray(); + } + public function syncData(bool $toModel = false) { if ($toModel) { diff --git a/resources/views/livewire/server/show.blade.php b/resources/views/livewire/server/show.blade.php index 5aed0b4e2..0d1d5e681 100644 --- a/resources/views/livewire/server/show.blade.php +++ b/resources/views/livewire/server/show.blade.php @@ -88,7 +88,7 @@
Date: Wed, 4 Dec 2024 12:43:52 +0100 Subject: [PATCH 65/75] test: setup database for upcoming tests --- app/Models/Application.php | 3 +- app/Models/Server.php | 3 +- config/database.php | 16 ++++ database/factories/ApplicationFactory.php | 22 ++++++ database/factories/ServerFactory.php | 17 ++++ phpunit.xml | 4 +- tests/Feature/ExecuteContainerCommandTest.php | 57 ++++++++++++++ tests/Traits/HandlesTestDatabase.php | 78 +++++++++++++++++++ 8 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 database/factories/ApplicationFactory.php create mode 100644 database/factories/ServerFactory.php create mode 100644 tests/Feature/ExecuteContainerCommandTest.php create mode 100644 tests/Traits/HandlesTestDatabase.php diff --git a/app/Models/Application.php b/app/Models/Application.php index c284528f1..a68c1d54a 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Enums\ApplicationDeploymentStatus; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Process\InvokedProcess; @@ -104,7 +105,7 @@ use Visus\Cuid2\Cuid2; class Application extends BaseModel { - use SoftDeletes; + use HasFactory, SoftDeletes; private static $parserVersion = '4'; diff --git a/app/Models/Server.php b/app/Models/Server.php index 83b91b254..e0a66c58b 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -11,6 +11,7 @@ use App\Notifications\Server\Reachable; use App\Notifications\Server\Unreachable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; @@ -48,7 +49,7 @@ use Symfony\Component\Yaml\Yaml; class Server extends BaseModel { - use SchemalessAttributesTrait, SoftDeletes; + use HasFactory, SchemalessAttributesTrait, SoftDeletes; public static $batch_counter = 0; diff --git a/config/database.php b/config/database.php index f48a68082..6f4acbfd2 100644 --- a/config/database.php +++ b/config/database.php @@ -49,6 +49,22 @@ return [ 'search_path' => 'public', 'sslmode' => 'prefer', ], + + 'testing' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_TEST_URL'), + 'host' => env('DB_TEST_HOST', 'postgres'), + 'port' => env('DB_TEST_PORT', '5432'), + 'database' => env('DB_TEST_DATABASE', 'coolify_test'), + 'username' => env('DB_TEST_USERNAME', 'coolify'), + 'password' => env('DB_TEST_PASSWORD', 'password'), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + ], /* diff --git a/database/factories/ApplicationFactory.php b/database/factories/ApplicationFactory.php new file mode 100644 index 000000000..ded507c56 --- /dev/null +++ b/database/factories/ApplicationFactory.php @@ -0,0 +1,22 @@ + fake()->unique()->name(), + 'destination_id' => 1, + 'git_repository' => fake()->url(), + 'git_branch' => fake()->word(), + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => 1, + 'destination_id' => 1, + ]; + } +} diff --git a/database/factories/ServerFactory.php b/database/factories/ServerFactory.php new file mode 100644 index 000000000..29546bf56 --- /dev/null +++ b/database/factories/ServerFactory.php @@ -0,0 +1,17 @@ + fake()->unique()->name(), + 'ip' => fake()->unique()->ipv4(), + 'private_key_id' => 1, + ]; + } +} diff --git a/phpunit.xml b/phpunit.xml index 45cb69439..f1c2be92d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,8 +13,8 @@ - - + + diff --git a/tests/Feature/ExecuteContainerCommandTest.php b/tests/Feature/ExecuteContainerCommandTest.php new file mode 100644 index 000000000..6d485fe65 --- /dev/null +++ b/tests/Feature/ExecuteContainerCommandTest.php @@ -0,0 +1,57 @@ +shouldSetUpDatabase()) { + $this->setUpTestDatabase(); + } + // Create test data + $this->user = User::factory()->create(); + $this->team = $this->user->teams()->first(); + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->application = Application::factory()->create(); + + // Login the user + $this->actingAs($this->user); + } + + protected function tearDown(): void + { + if ($this->shouldSetUpDatabase()) { + $this->tearDownTestDatabase(); + } + parent::tearDown(); + } + + private function shouldSetUpDatabase(): bool + { + return in_array($this->name(), [ + 'it_allows_valid_container_access', + 'it_prevents_cross_server_container_access', + ]); + } +} diff --git a/tests/Traits/HandlesTestDatabase.php b/tests/Traits/HandlesTestDatabase.php new file mode 100644 index 000000000..adb577e7c --- /dev/null +++ b/tests/Traits/HandlesTestDatabase.php @@ -0,0 +1,78 @@ +createTestDatabase($database); + + // Run migrations + Artisan::call('migrate:fresh', [ + '--database' => 'testing', + '--seed' => false, + ]); + } catch (\Exception $e) { + $this->tearDownTestDatabase(); + throw $e; + } + } + + protected function tearDownTestDatabase(): void + { + try { + // Drop test database + $database = config('database.connections.testing.database'); + $this->dropTestDatabase($database); + } catch (\Exception $e) { + // Log error but don't throw + error_log('Failed to tear down test database: '.$e->getMessage()); + } + } + + protected function createTestDatabase($database) + { + try { + // Connect to postgres database to create/drop test database + config(['database.connections.pgsql.database' => 'postgres']); + DB::purge('pgsql'); + DB::reconnect('pgsql'); + + // Drop if exists and create new database + DB::connection('pgsql')->statement("DROP DATABASE IF EXISTS $database WITH (FORCE);"); + DB::connection('pgsql')->statement("CREATE DATABASE $database;"); + + // Switch back to testing connection + DB::disconnect('pgsql'); + DB::reconnect('testing'); + } catch (\Exception $e) { + $this->tearDownTestDatabase(); + throw new \Exception('Could not create test database: '.$e->getMessage()); + } + } + + protected function dropTestDatabase($database) + { + try { + // Connect to postgres database to drop test database + config(['database.connections.pgsql.database' => 'postgres']); + DB::purge('pgsql'); + DB::reconnect('pgsql'); + + // Drop the test database + DB::connection('pgsql')->statement("DROP DATABASE IF EXISTS $database WITH (FORCE);"); + + DB::disconnect('pgsql'); + } catch (\Exception $e) { + // Log error but don't throw + error_log('Failed to drop test database: '.$e->getMessage()); + } + } +} From 029e8954c5e5b3dd63145b1d1047462f201ccffd Mon Sep 17 00:00:00 2001 From: Eirik H Date: Wed, 4 Dec 2024 13:19:00 +0100 Subject: [PATCH 66/75] Support PopOS as server PopOS is already added as supported for the main Coolify instance, but fails when adding a PopOS machine as an additional server. --- bootstrap/helpers/constants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 303fcab8e..b568e090c 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -46,7 +46,7 @@ const SPECIFIC_SERVICES = [ // Based on /etc/os-release const SUPPORTED_OS = [ - 'ubuntu debian raspbian', + 'ubuntu debian raspbian pop', 'centos fedora rhel ol rocky amzn almalinux', 'sles opensuse-leap opensuse-tumbleweed', 'arch', From 94c264c6d7ce5accdc720201a5cbadf011dc87d8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 4 Dec 2024 13:49:19 +0100 Subject: [PATCH 67/75] Fix deployment status border color in deployment index view --- .../livewire/project/application/deployment/index.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/project/application/deployment/index.blade.php b/resources/views/livewire/project/application/deployment/index.blade.php index f6fdb64ab..3fa52b7f3 100644 --- a/resources/views/livewire/project/application/deployment/index.blade.php +++ b/resources/views/livewire/project/application/deployment/index.blade.php @@ -32,7 +32,7 @@ @forelse ($deployments as $deployment)
+ 'border-white border-dashed ' => data_get($deployment, 'status') === 'in_progress' || data_get($deployment, 'status') === 'cancelled-by-user', 'border-error border-dashed ' => From f535ed2359f9400ded0eed439360e059bb28f41b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 4 Dec 2024 14:00:49 +0100 Subject: [PATCH 68/75] Enhance modal input component and storage forms - Added 'minWidth' attribute to modal input component for better responsiveness. - Updated modal input instantiation in storage view to set a minimum width of 64rem. - Improved layout and user guidance in the storage add forms by adding descriptive text for volume, file, and directory mounts. - Changed button labels from 'Save' to 'Add' for clarity in storage forms. --- .../views/components/modal-input.blade.php | 3 +- .../project/service/storage.blade.php | 2 +- .../project/shared/storages/add.blade.php | 50 ++++++++++++------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/resources/views/components/modal-input.blade.php b/resources/views/components/modal-input.blade.php index c090037d5..acd3b8bdf 100644 --- a/resources/views/components/modal-input.blade.php +++ b/resources/views/components/modal-input.blade.php @@ -7,6 +7,7 @@ 'action' => 'delete', 'content' => null, 'closeOutside' => true, + 'minWidth' => '36rem', ])
@@ -40,7 +41,7 @@ x-transition:leave="ease-in duration-100" x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100" x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95" - class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[36rem] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300"> + class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[{{ $minWidth }}] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300">

{{ $title }}