{{ $isLoadingContainers ? 'Finding available servers and containers…' : 'Choose a server or container. The terminal will open after you select a target.' }}
@@ -126,7 +125,7 @@
@endif
-
+
@if ($isLoadingContainers)
diff --git a/routes/web.php b/routes/web.php
index 49a87790c..40869b4b9 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -2,6 +2,7 @@
use App\Http\Controllers\Controller;
use App\Http\Controllers\OauthController;
+use App\Http\Controllers\ProfileAvatarController;
use App\Http\Controllers\UploadController;
use App\Livewire\Admin\Index as AdminIndex;
use App\Livewire\Boarding\Index as BoardingIndex;
@@ -159,6 +160,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/settings/scheduled-jobs', SettingsScheduledJobs::class)->name('settings.scheduled-jobs');
Route::get('/profile', ProfileIndex::class)->name('profile');
+ Route::get('/profile/avatar', ProfileAvatarController::class)->name('profile.avatar');
Route::get('/profile/appearance', ProfileAppearance::class)->name('profile.appearance');
Route::prefix('tags')->group(function () {
diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json
index 71d7da29c..a4d1fb655 100644
--- a/templates/service-templates-latest.json
+++ b/templates/service-templates-latest.json
@@ -421,7 +421,7 @@
"buzz": {
"documentation": "https://github.com/block/buzz?utm_source=coolify.io",
"slogan": "Self-hostable workspace where humans and AI agents collaborate on a Nostr relay you own.",
- "compose": "c2VydmljZXM6CiAgYnV6ei1naXQtaW5pdDoKICAgIGltYWdlOiAnYWxwaW5lOjMuMjEnCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LWdpdC1kYXRhOi9kYXRhL2dpdCcKICAgIGNvbW1hbmQ6CiAgICAgIC0gL2Jpbi9zaAogICAgICAtICctZWMnCiAgICAgIC0gJ2Nob3duIC1SIDEwMDA6MTAwMCAvZGF0YS9naXQnCiAgICByZXN0YXJ0OiAnbm8nCiAgICBleGNsdWRlX2Zyb21faGM6IHRydWUKICBidXp6OgogICAgaW1hZ2U6ICdnaGNyLmlvL2Jsb2NrL2J1eno6JHtCVVpaX1RBRzotbWFpbn0nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9CVVpaXzMwMDAKICAgICAgLSAnQlVaWl9CSU5EX0FERFI9MC4wLjAuMDozMDAwJwogICAgICAtIEJVWlpfSEVBTFRIX1BPUlQ9ODA4MAogICAgICAtIEJVWlpfTUVUUklDU19QT1JUPTkxMDIKICAgICAgLSAnUkVMQVlfVVJMPXdzczovLyR7U0VSVklDRV9GUUROX0JVWlp9JwogICAgICAtICdCVVpaX01FRElBX0JBU0VfVVJMPSR7U0VSVklDRV9VUkxfQlVaWn0vbWVkaWEnCiAgICAgIC0gJ0JVWlpfTUVESUFfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9CVVpafScKICAgICAgLSAnQlVaWl9DT1JTX09SSUdJTlM9JHtTRVJWSUNFX1VSTF9CVVpafScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTX0RCOi1idXp6fScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vOiR7U0VSVklDRV9QQVNTV09SRF9SRURJU31AcmVkaXM6NjM3OScKICAgICAgLSAnQlVaWl9TM19FTkRQT0lOVD1odHRwOi8vbWluaW86OTAwMCcKICAgICAgLSAnQlVaWl9TM19BQ0NFU1NfS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnQlVaWl9TM19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICAgIC0gJ0JVWlpfUzNfQlVDS0VUPSR7QlVaWl9TM19CVUNLRVQ6LWJ1enotbWVkaWF9JwogICAgICAtIEJVWlpfR0lUX1JFUE9fUEFUSD0vZGF0YS9naXQKICAgICAgLSAnQlVaWl9SRUxBWV9QUklWQVRFX0tFWT0ke1NFUlZJQ0VfSEVYXzY0X1JFTEFZS0VZfScKICAgICAgLSAnQlVaWl9HSVRfSE9PS19ITUFDX1NFQ1JFVD0ke1NFUlZJQ0VfSEVYXzY0X0dJVEhNQUN9JwogICAgICAtICdCVVpaX0FVVE9fTUlHUkFURT0ke0JVWlpfQVVUT19NSUdSQVRFOi10cnVlfScKICAgICAgLSAnQlVaWl9HSVRfQ09ORk9STUFOQ0VfUFJPQkU9JHtCVVpaX0dJVF9DT05GT1JNQU5DRV9QUk9CRTotdHJ1ZX0nCiAgICAgIC0gJ0JVWlpfUkVRVUlSRV9BVVRIX1RPS0VOPSR7QlVaWl9SRVFVSVJFX0FVVEhfVE9LRU46LXRydWV9JwogICAgICAtICdCVVpaX1JFUVVJUkVfUkVMQVlfTUVNQkVSU0hJUD0ke0JVWlpfUkVRVUlSRV9SRUxBWV9NRU1CRVJTSElQOi1mYWxzZX0nCiAgICAgIC0gJ0JVWlpfQUxMT1dfTklQX09BX0FVVEg9JHtCVVpaX0FMTE9XX05JUF9PQV9BVVRIOi10cnVlfScKICAgICAgLSAnUkVMQVlfT1dORVJfUFVCS0VZPSR7UkVMQVlfT1dORVJfUFVCS0VZfScKICAgICAgLSAnUlVTVF9MT0c9JHtSVVNUX0xPRzotYnV6el9yZWxheT1pbmZvLGJ1enpfZGI9aW5mbyxidXp6X2F1dGg9aW5mbyxidXp6X3B1YnN1Yj1pbmZvLHRvd2VyX2h0dHA9aW5mb30nCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LWdpdC1kYXRhOi9kYXRhL2dpdCcKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1pbmlvLWluaXQ6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2NvbXBsZXRlZF9zdWNjZXNzZnVsbHkKICAgICAgYnV6ei1naXQtaW5pdDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfY29tcGxldGVkX3N1Y2Nlc3NmdWxseQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdjdXJsIC1mc1MgaHR0cDovLzEyNy4wLjAuMTo4MDgwL19yZWFkaW5lc3MgPi9kZXYvbnVsbCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiA0NXMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTctYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1idXp6fScKICAgICAgLSBQR0RBVEE9L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhL3BnZGF0YQogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1wb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LWFscGluZScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tYXBwZW5kb25seScKICAgICAgLSAneWVzJwogICAgICAtICctLXJlcXVpcmVwYXNzJwogICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3JlZGlzLWNsaSAtYSAiJCR7UkVESVNfUEFTU1dPUkR9IiBwaW5nIHwgZ3JlcCAtcSBQT05HJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogM3MKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiA1cwogIG1pbmlvOgogICAgaW1hZ2U6ICdtaW5pby9taW5pbzpSRUxFQVNFLjIwMjUtMDktMDdUMTYtMTMtMDlaJwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2J1enotbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICBtaW5pby1pbml0OgogICAgaW1hZ2U6ICdtaW5pby9tYzpSRUxFQVNFLjIwMjUtMDgtMTNUMDgtMzUtNDFaJwogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtICdCVVpaX1MzX0FDQ0VTU19LRVk9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdCVVpaX1MzX1NFQ1JFVF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnQlVaWl9TM19CVUNLRVQ9JHtCVVpaX1MzX0JVQ0tFVDotYnV6ei1tZWRpYX0nCiAgICBlbnRyeXBvaW50OgogICAgICAtIC9iaW4vc2gKICAgICAgLSAnLWV1YycKICAgICAgLSAibWMgYWxpYXMgc2V0IGxvY2FsIGh0dHA6Ly9taW5pbzo5MDAwIFwiJCR7QlVaWl9TM19BQ0NFU1NfS0VZfVwiIFwiJCR7QlVaWl9TM19TRUNSRVRfS0VZfVwiXG5tYyBtYiAtLWlnbm9yZS1leGlzdGluZyBcImxvY2FsLyQke0JVWlpfUzNfQlVDS0VUfVwiXG5tYyBhbm9ueW1vdXMgc2V0IG5vbmUgXCJsb2NhbC8kJHtCVVpaX1MzX0JVQ0tFVH1cIiIKICAgIHJlc3RhcnQ6ICdubycKICAgIGV4Y2x1ZGVfZnJvbV9oYzogdHJ1ZQo=",
+ "compose": "c2VydmljZXM6CiAgYnV6ejoKICAgIGltYWdlOiAnZ2hjci5pby9ibG9jay9idXp6OiR7QlVaWl9UQUc6LW1haW59JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfQlVaWl8zMDAwCiAgICAgIC0gJ0JVWlpfQklORF9BRERSPTAuMC4wLjA6MzAwMCcKICAgICAgLSBCVVpaX0hFQUxUSF9QT1JUPTgwODAKICAgICAgLSBCVVpaX01FVFJJQ1NfUE9SVD05MTAyCiAgICAgIC0gJ1JFTEFZX1VSTD13c3M6Ly8ke1NFUlZJQ0VfRlFETl9CVVpafScKICAgICAgLSAnQlVaWl9NRURJQV9CQVNFX1VSTD0ke1NFUlZJQ0VfVVJMX0JVWlp9L21lZGlhJwogICAgICAtICdCVVpaX01FRElBX1NFUlZFUl9ET01BSU49JHtTRVJWSUNFX0ZRRE5fQlVaWn0nCiAgICAgIC0gJ0JVWlpfQ09SU19PUklHSU5TPSR7U0VSVklDRV9VUkxfQlVaWn0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotYnV6en0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovLzoke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9QHJlZGlzOjYzNzknCiAgICAgIC0gJ0JVWlpfUzNfRU5EUE9JTlQ9aHR0cDovL21pbmlvOjkwMDAnCiAgICAgIC0gJ0JVWlpfUzNfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0JVWlpfUzNfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdCVVpaX1MzX0JVQ0tFVD0ke0JVWlpfUzNfQlVDS0VUOi1idXp6LW1lZGlhfScKICAgICAgLSBCVVpaX0dJVF9SRVBPX1BBVEg9L2RhdGEvZ2l0CiAgICAgIC0gJ0JVWlpfUkVMQVlfUFJJVkFURV9LRVk9JHtTRVJWSUNFX0hFWF82NF9SRUxBWUtFWX0nCiAgICAgIC0gJ0JVWlpfR0lUX0hPT0tfSE1BQ19TRUNSRVQ9JHtTRVJWSUNFX0hFWF82NF9HSVRITUFDfScKICAgICAgLSAnQlVaWl9BVVRPX01JR1JBVEU9JHtCVVpaX0FVVE9fTUlHUkFURTotdHJ1ZX0nCiAgICAgIC0gJ0JVWlpfR0lUX0NPTkZPUk1BTkNFX1BST0JFPSR7QlVaWl9HSVRfQ09ORk9STUFOQ0VfUFJPQkU6LXRydWV9JwogICAgICAtICdCVVpaX1JFUVVJUkVfQVVUSF9UT0tFTj0ke0JVWlpfUkVRVUlSRV9BVVRIX1RPS0VOOi10cnVlfScKICAgICAgLSAnQlVaWl9SRVFVSVJFX1JFTEFZX01FTUJFUlNISVA9JHtCVVpaX1JFUVVJUkVfUkVMQVlfTUVNQkVSU0hJUDotZmFsc2V9JwogICAgICAtICdCVVpaX0FMTE9XX05JUF9PQV9BVVRIPSR7QlVaWl9BTExPV19OSVBfT0FfQVVUSDotdHJ1ZX0nCiAgICAgIC0gJ1JFTEFZX09XTkVSX1BVQktFWT0ke1JFTEFZX09XTkVSX1BVQktFWX0nCiAgICAgIC0gJ1JVU1RfTE9HPSR7UlVTVF9MT0c6LWJ1enpfcmVsYXk9aW5mbyxidXp6X2RiPWluZm8sYnV6el9hdXRoPWluZm8sYnV6el9wdWJzdWI9aW5mbyx0b3dlcl9odHRwPWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1naXQtZGF0YTovZGF0YS9naXQnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtaW5pby1pbml0OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9jb21wbGV0ZWRfc3VjY2Vzc2Z1bGx5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ2N1cmwgLWZzUyBodHRwOi8vMTI3LjAuMC4xOjgwODAvX3JlYWRpbmVzcyA+L2Rldi9udWxsIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDQ1cwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNy1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWJ1enp9JwogICAgICAtIFBHREFUQT0vdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEvcGdkYXRhCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LXBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEyCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDoKICAgICAgLSByZWRpcy1zZXJ2ZXIKICAgICAgLSAnLS1hcHBlbmRvbmx5JwogICAgICAtICd5ZXMnCiAgICAgIC0gJy0tcmVxdWlyZXBhc3MnCiAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2J1enotcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncmVkaXMtY2xpIC1hICIkJHtSRURJU19QQVNTV09SRH0iIHBpbmcgfCBncmVwIC1xIFBPTkcnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAzcwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDVzCiAgbWluaW86CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOlJFTEVBU0UuMjAyNS0wOS0wN1QxNi0xMy0wOVonCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tY29uc29sZS1hZGRyZXNzICI6OTAwMSInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1taW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjkwMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogJ21pbmlvL21jOlJFTEVBU0UuMjAyNS0wOC0xM1QwOC0zNS00MVonCiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0JVWlpfUzNfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0JVWlpfUzNfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdCVVpaX1MzX0JVQ0tFVD0ke0JVWlpfUzNfQlVDS0VUOi1idXp6LW1lZGlhfScKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2Jpbi9zaAogICAgICAtICctZXVjJwogICAgICAtICJtYyBhbGlhcyBzZXQgbG9jYWwgaHR0cDovL21pbmlvOjkwMDAgXCIkJHtCVVpaX1MzX0FDQ0VTU19LRVl9XCIgXCIkJHtCVVpaX1MzX1NFQ1JFVF9LRVl9XCJcbm1jIG1iIC0taWdub3JlLWV4aXN0aW5nIFwibG9jYWwvJCR7QlVaWl9TM19CVUNLRVR9XCJcbm1jIGFub255bW91cyBzZXQgbm9uZSBcImxvY2FsLyQke0JVWlpfUzNfQlVDS0VUfVwiIgogICAgcmVzdGFydDogJ25vJwogICAgZXhjbHVkZV9mcm9tX2hjOiB0cnVlCg==",
"tags": [
"buzz",
"chat",
@@ -435,7 +435,7 @@
"category": "messaging",
"logo": "svgs/buzz.svg",
"minversion": "0.0.0",
- "template_last_updated_at": "2026-07-23T18:52:29+02:00",
+ "template_last_updated_at": "2026-07-23T17:25:53+02:00",
"port": "3000"
},
"calibre-web-automated-book-downloader": {
@@ -2764,7 +2764,7 @@
"category": "devtools",
"logo": "svgs/librespeed.png",
"minversion": "0.0.0",
- "template_last_updated_at": "2026-02-25T23:48:15+00:00",
+ "template_last_updated_at": "2026-02-25T23:48:15Z",
"port": "82"
},
"libretranslate": {
@@ -2901,7 +2901,7 @@
"category": "auth",
"logo": "svgs/logto_dark.svg",
"minversion": "0.0.0",
- "template_last_updated_at": "2026-04-01T13:19:47+00:00"
+ "template_last_updated_at": "2026-04-01T13:19:47Z"
},
"lowcoder": {
"documentation": "https://docs.lowcoder.cloud/?utm_source=coolify.io",
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 002017348..d9500219f 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -421,7 +421,7 @@
"buzz": {
"documentation": "https://github.com/block/buzz?utm_source=coolify.io",
"slogan": "Self-hostable workspace where humans and AI agents collaborate on a Nostr relay you own.",
- "compose": "c2VydmljZXM6CiAgYnV6ejoKICAgIGltYWdlOiAnZ2hjci5pby9ibG9jay9idXp6OiR7QlVaWl9UQUc6LW1haW59JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JVWlpfMzAwMAogICAgICAtICdCVVpaX0JJTkRfQUREUj0wLjAuMC4wOjMwMDAnCiAgICAgIC0gQlVaWl9IRUFMVEhfUE9SVD04MDgwCiAgICAgIC0gQlVaWl9NRVRSSUNTX1BPUlQ9OTEwMgogICAgICAtICdSRUxBWV9VUkw9d3NzOi8vJHtTRVJWSUNFX0ZRRE5fQlVaWn0nCiAgICAgIC0gJ0JVWlpfTUVESUFfQkFTRV9VUkw9aHR0cHM6Ly8ke1NFUlZJQ0VfRlFETl9CVVpafS9tZWRpYScKICAgICAgLSAnQlVaWl9NRURJQV9TRVJWRVJfRE9NQUlOPSR7U0VSVklDRV9GUUROX0JVWlp9JwogICAgICAtICdCVVpaX0NPUlNfT1JJR0lOUz1odHRwczovLyR7U0VSVklDRV9GUUROX0JVWlp9JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU306JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNfREI6LWJ1enp9JwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly86JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfUByZWRpczo2Mzc5JwogICAgICAtICdCVVpaX1MzX0VORFBPSU5UPWh0dHA6Ly9taW5pbzo5MDAwJwogICAgICAtICdCVVpaX1MzX0FDQ0VTU19LRVk9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdCVVpaX1MzX1NFQ1JFVF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnQlVaWl9TM19CVUNLRVQ9JHtCVVpaX1MzX0JVQ0tFVDotYnV6ei1tZWRpYX0nCiAgICAgIC0gQlVaWl9HSVRfUkVQT19QQVRIPS9kYXRhL2dpdAogICAgICAtICdCVVpaX1JFTEFZX1BSSVZBVEVfS0VZPSR7U0VSVklDRV9IRVhfNjRfUkVMQVlLRVl9JwogICAgICAtICdCVVpaX0dJVF9IT09LX0hNQUNfU0VDUkVUPSR7U0VSVklDRV9IRVhfNjRfR0lUSE1BQ30nCiAgICAgIC0gJ0JVWlpfQVVUT19NSUdSQVRFPSR7QlVaWl9BVVRPX01JR1JBVEU6LXRydWV9JwogICAgICAtICdCVVpaX0dJVF9DT05GT1JNQU5DRV9QUk9CRT0ke0JVWlpfR0lUX0NPTkZPUk1BTkNFX1BST0JFOi10cnVlfScKICAgICAgLSAnQlVaWl9SRVFVSVJFX0FVVEhfVE9LRU49JHtCVVpaX1JFUVVJUkVfQVVUSF9UT0tFTjotdHJ1ZX0nCiAgICAgIC0gJ0JVWlpfUkVRVUlSRV9SRUxBWV9NRU1CRVJTSElQPSR7QlVaWl9SRVFVSVJFX1JFTEFZX01FTUJFUlNISVA6LWZhbHNlfScKICAgICAgLSAnQlVaWl9BTExPV19OSVBfT0FfQVVUSD0ke0JVWlpfQUxMT1dfTklQX09BX0FVVEg6LXRydWV9JwogICAgICAtICdSRUxBWV9PV05FUl9QVUJLRVk9JHtSRUxBWV9PV05FUl9QVUJLRVl9JwogICAgICAtICdSVVNUX0xPRz0ke1JVU1RfTE9HOi1idXp6X3JlbGF5PWluZm8sYnV6el9kYj1pbmZvLGJ1enpfYXV0aD1pbmZvLGJ1enpfcHVic3ViPWluZm8sdG93ZXJfaHR0cD1pbmZvfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2J1enotZ2l0LWRhdGE6L2RhdGEvZ2l0JwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW8taW5pdDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfY29tcGxldGVkX3N1Y2Nlc3NmdWxseQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdjdXJsIC1mc1MgaHR0cDovLzEyNy4wLjAuMTo4MDgwL19yZWFkaW5lc3MgPi9kZXYvbnVsbCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiA0NXMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTctYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1idXp6fScKICAgICAgLSBQR0RBVEE9L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhL3BnZGF0YQogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1wb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LWFscGluZScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tYXBwZW5kb25seScKICAgICAgLSAneWVzJwogICAgICAtICctLXJlcXVpcmVwYXNzJwogICAgICAtICcke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3JlZGlzLWNsaSAtYSAiJCR7UkVESVNfUEFTU1dPUkR9IiBwaW5nIHwgZ3JlcCAtcSBQT05HJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogM3MKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiA1cwogIG1pbmlvOgogICAgaW1hZ2U6ICdtaW5pby9taW5pbzpSRUxFQVNFLjIwMjUtMDktMDdUMTYtMTMtMDlaJwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2J1enotbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo5MDAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMTIKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICBtaW5pby1pbml0OgogICAgaW1hZ2U6ICdtaW5pby9tYzpSRUxFQVNFLjIwMjUtMDgtMTNUMDgtMzUtNDFaJwogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtICdCVVpaX1MzX0FDQ0VTU19LRVk9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdCVVpaX1MzX1NFQ1JFVF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnQlVaWl9TM19CVUNLRVQ9JHtCVVpaX1MzX0JVQ0tFVDotYnV6ei1tZWRpYX0nCiAgICBlbnRyeXBvaW50OgogICAgICAtIC9iaW4vc2gKICAgICAgLSAnLWV1YycKICAgICAgLSAibWMgYWxpYXMgc2V0IGxvY2FsIGh0dHA6Ly9taW5pbzo5MDAwIFwiJCR7QlVaWl9TM19BQ0NFU1NfS0VZfVwiIFwiJCR7QlVaWl9TM19TRUNSRVRfS0VZfVwiXG5tYyBtYiAtLWlnbm9yZS1leGlzdGluZyBcImxvY2FsLyQke0JVWlpfUzNfQlVDS0VUfVwiXG5tYyBhbm9ueW1vdXMgc2V0IG5vbmUgXCJsb2NhbC8kJHtCVVpaX1MzX0JVQ0tFVH1cIiIKICAgIHJlc3RhcnQ6ICdubycKICAgIGV4Y2x1ZGVfZnJvbV9oYzogdHJ1ZQo=",
+ "compose": "c2VydmljZXM6CiAgYnV6ejoKICAgIGltYWdlOiAnZ2hjci5pby9ibG9jay9idXp6OiR7QlVaWl9UQUc6LW1haW59JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JVWlpfMzAwMAogICAgICAtICdCVVpaX0JJTkRfQUREUj0wLjAuMC4wOjMwMDAnCiAgICAgIC0gQlVaWl9IRUFMVEhfUE9SVD04MDgwCiAgICAgIC0gQlVaWl9NRVRSSUNTX1BPUlQ9OTEwMgogICAgICAtICdSRUxBWV9VUkw9d3NzOi8vJHtTRVJWSUNFX0ZRRE5fQlVaWn0nCiAgICAgIC0gJ0JVWlpfTUVESUFfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fQlVaWn0vbWVkaWEnCiAgICAgIC0gJ0JVWlpfTUVESUFfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9CVVpafScKICAgICAgLSAnQlVaWl9DT1JTX09SSUdJTlM9JHtTRVJWSUNFX0ZRRE5fQlVaWn0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU19EQjotYnV6en0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovLzoke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9QHJlZGlzOjYzNzknCiAgICAgIC0gJ0JVWlpfUzNfRU5EUE9JTlQ9aHR0cDovL21pbmlvOjkwMDAnCiAgICAgIC0gJ0JVWlpfUzNfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0JVWlpfUzNfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdCVVpaX1MzX0JVQ0tFVD0ke0JVWlpfUzNfQlVDS0VUOi1idXp6LW1lZGlhfScKICAgICAgLSBCVVpaX0dJVF9SRVBPX1BBVEg9L2RhdGEvZ2l0CiAgICAgIC0gJ0JVWlpfUkVMQVlfUFJJVkFURV9LRVk9JHtTRVJWSUNFX0hFWF82NF9SRUxBWUtFWX0nCiAgICAgIC0gJ0JVWlpfR0lUX0hPT0tfSE1BQ19TRUNSRVQ9JHtTRVJWSUNFX0hFWF82NF9HSVRITUFDfScKICAgICAgLSAnQlVaWl9BVVRPX01JR1JBVEU9JHtCVVpaX0FVVE9fTUlHUkFURTotdHJ1ZX0nCiAgICAgIC0gJ0JVWlpfR0lUX0NPTkZPUk1BTkNFX1BST0JFPSR7QlVaWl9HSVRfQ09ORk9STUFOQ0VfUFJPQkU6LXRydWV9JwogICAgICAtICdCVVpaX1JFUVVJUkVfQVVUSF9UT0tFTj0ke0JVWlpfUkVRVUlSRV9BVVRIX1RPS0VOOi10cnVlfScKICAgICAgLSAnQlVaWl9SRVFVSVJFX1JFTEFZX01FTUJFUlNISVA9JHtCVVpaX1JFUVVJUkVfUkVMQVlfTUVNQkVSU0hJUDotZmFsc2V9JwogICAgICAtICdCVVpaX0FMTE9XX05JUF9PQV9BVVRIPSR7QlVaWl9BTExPV19OSVBfT0FfQVVUSDotdHJ1ZX0nCiAgICAgIC0gJ1JFTEFZX09XTkVSX1BVQktFWT0ke1JFTEFZX09XTkVSX1BVQktFWX0nCiAgICAgIC0gJ1JVU1RfTE9HPSR7UlVTVF9MT0c6LWJ1enpfcmVsYXk9aW5mbyxidXp6X2RiPWluZm8sYnV6el9hdXRoPWluZm8sYnV6el9wdWJzdWI9aW5mbyx0b3dlcl9odHRwPWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1naXQtZGF0YTovZGF0YS9naXQnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtaW5pby1pbml0OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9jb21wbGV0ZWRfc3VjY2Vzc2Z1bGx5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ2N1cmwgLWZzUyBodHRwOi8vMTI3LjAuMC4xOjgwODAvX3JlYWRpbmVzcyA+L2Rldi9udWxsIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDQ1cwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNy1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWJ1enp9JwogICAgICAtIFBHREFUQT0vdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEvcGdkYXRhCiAgICB2b2x1bWVzOgogICAgICAtICdidXp6LXBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEyCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDoKICAgICAgLSByZWRpcy1zZXJ2ZXIKICAgICAgLSAnLS1hcHBlbmRvbmx5JwogICAgICAtICd5ZXMnCiAgICAgIC0gJy0tcmVxdWlyZXBhc3MnCiAgICAgIC0gJyR7U0VSVklDRV9QQVNTV09SRF9SRURJU30nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUkVESVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JFRElTfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2J1enotcmVkaXMtZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncmVkaXMtY2xpIC1hICIkJHtSRURJU19QQVNTV09SRH0iIHBpbmcgfCBncmVwIC1xIFBPTkcnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAzcwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDVzCiAgbWluaW86CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOlJFTEVBU0UuMjAyNS0wOS0wN1QxNi0xMy0wOVonCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tY29uc29sZS1hZGRyZXNzICI6OTAwMSInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgdm9sdW1lczoKICAgICAgLSAnYnV6ei1taW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjkwMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMgogICAgICBzdGFydF9wZXJpb2Q6IDEwcwogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogJ21pbmlvL21jOlJFTEVBU0UuMjAyNS0wOC0xM1QwOC0zNS00MVonCiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0JVWlpfUzNfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ0JVWlpfUzNfU0VDUkVUX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdCVVpaX1MzX0JVQ0tFVD0ke0JVWlpfUzNfQlVDS0VUOi1idXp6LW1lZGlhfScKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2Jpbi9zaAogICAgICAtICctZXVjJwogICAgICAtICJtYyBhbGlhcyBzZXQgbG9jYWwgaHR0cDovL21pbmlvOjkwMDAgXCIkJHtCVVpaX1MzX0FDQ0VTU19LRVl9XCIgXCIkJHtCVVpaX1MzX1NFQ1JFVF9LRVl9XCJcbm1jIG1iIC0taWdub3JlLWV4aXN0aW5nIFwibG9jYWwvJCR7QlVaWl9TM19CVUNLRVR9XCJcbm1jIGFub255bW91cyBzZXQgbm9uZSBcImxvY2FsLyQke0JVWlpfUzNfQlVDS0VUfVwiIgogICAgcmVzdGFydDogJ25vJwogICAgZXhjbHVkZV9mcm9tX2hjOiB0cnVlCg==",
"tags": [
"buzz",
"chat",
@@ -435,7 +435,7 @@
"category": "messaging",
"logo": "svgs/buzz.svg",
"minversion": "0.0.0",
- "template_last_updated_at": null,
+ "template_last_updated_at": "2026-07-23T17:25:53+02:00",
"port": "3000"
},
"calibre-web-automated-book-downloader": {
@@ -2764,7 +2764,7 @@
"category": "devtools",
"logo": "svgs/librespeed.png",
"minversion": "0.0.0",
- "template_last_updated_at": "2026-02-25T23:48:15+00:00",
+ "template_last_updated_at": "2026-02-25T23:48:15Z",
"port": "82"
},
"libretranslate": {
@@ -2901,7 +2901,7 @@
"category": "auth",
"logo": "svgs/logto_dark.svg",
"minversion": "0.0.0",
- "template_last_updated_at": "2026-04-01T13:19:47+00:00"
+ "template_last_updated_at": "2026-04-01T13:19:47Z"
},
"lowcoder": {
"documentation": "https://docs.lowcoder.cloud/?utm_source=coolify.io",
diff --git a/tests/Feature/ProfileAvatarTest.php b/tests/Feature/ProfileAvatarTest.php
new file mode 100644
index 000000000..04c158a34
--- /dev/null
+++ b/tests/Feature/ProfileAvatarTest.php
@@ -0,0 +1,131 @@
+ InstanceSettings::create([
+ 'id' => 0,
+ 'avatar_storage_type' => 'local',
+ ]));
+});
+
+it('compresses and stores an uploaded profile picture on the configured local storage', function () {
+ Storage::fake('local');
+ $user = User::factory()->create(['name' => 'Test User']);
+ $this->actingAs($user);
+
+ Livewire::test(Index::class)
+ ->set('avatar', UploadedFile::fake()->image('profile.png', 1200, 900))
+ ->call('uploadAvatar')
+ ->assertHasNoErrors();
+
+ $user->refresh();
+
+ expect($user->avatar_path)
+ ->toBe("avatars/{$user->id}/avatar.jpg")
+ ->and($user->avatar_storage_type)->toBe('local')
+ ->and($user->avatar_s3_storage_id)->toBeNull();
+
+ Storage::disk('local')->assertExists($user->avatar_path);
+
+ $image = getimagesizefromstring(Storage::disk('local')->get($user->avatar_path));
+
+ expect($image[0])->toBeLessThanOrEqual(256)
+ ->and($image[1])->toBeLessThanOrEqual(256)
+ ->and($image['mime'])->toBe('image/jpeg')
+ ->and(Storage::disk('local')->size($user->avatar_path))->toBeLessThan(100_000);
+});
+
+it('stores an already compressed browser JPEG without server image extensions', function () {
+ Storage::fake('local');
+ $user = User::factory()->create(['name' => 'Test User']);
+ $contents = base64_decode('/9j/4AAQSkZJRgABAQEAYABgAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gODAK/9sAQwAGBAUGBQQGBgUGBwcGCAoQCgoJCQoUDg8MEBcUGBgXFBYWGh0lHxobIxwWFiAsICMmJykqKRkfLTAtKDAlKCko/9sAQwEHBwcKCAoTCgoTKBoWGigoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo/8AAEQgAAgACAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+qaKKKAP/9k=');
+ $path = tempnam(sys_get_temp_dir(), 'avatar');
+ file_put_contents($path, $contents);
+ $upload = new UploadedFile($path, 'avatar.jpg', 'image/jpeg', null, true);
+
+ app(AvatarStorageService::class)->store($user, $upload);
+
+ expect(Storage::disk('local')->get("avatars/{$user->id}/avatar.jpg"))->toBe($contents);
+});
+
+it('serves the authenticated users profile picture', function () {
+ Storage::fake('local');
+ $user = User::factory()->create([
+ 'name' => 'Test User',
+ 'avatar_path' => 'avatars/1/avatar.jpg',
+ 'avatar_storage_type' => 'local',
+ ]);
+ Storage::disk('local')->put($user->avatar_path, 'avatar-content');
+
+ $this->withoutMiddleware()->actingAs($user)
+ ->get(route('profile.avatar'))
+ ->assertSuccessful()
+ ->assertHeader('content-type', 'image/jpeg');
+});
+
+it('removes the current profile picture', function () {
+ Storage::fake('local');
+ $user = User::factory()->create([
+ 'name' => 'Test User',
+ 'avatar_path' => 'avatars/1/avatar.jpg',
+ 'avatar_storage_type' => 'local',
+ ]);
+ Storage::disk('local')->put($user->avatar_path, 'avatar-content');
+ $this->actingAs($user);
+
+ Livewire::test(Index::class)
+ ->call('removeAvatar')
+ ->assertHasNoErrors();
+
+ expect($user->refresh()->avatar_path)->toBeNull();
+ Storage::disk('local')->assertMissing('avatars/1/avatar.jpg');
+});
+
+it('falls back cleanly when the avatars S3 storage no longer exists', function () {
+ $user = User::factory()->create([
+ 'name' => 'Test User',
+ 'avatar_path' => 'avatars/1/avatar.webp',
+ 'avatar_storage_type' => 's3',
+ 'avatar_s3_storage_id' => null,
+ ]);
+
+ $this->withoutMiddleware()->actingAs($user)
+ ->get(route('profile.avatar'))
+ ->assertNotFound();
+});
+
+it('renders the profile upload and user menu avatar', function () {
+ $profile = file_get_contents(resource_path('views/livewire/profile/index.blade.php'));
+ $menu = file_get_contents(resource_path('views/components/top-user-menu.blade.php'));
+
+ expect($profile)
+ ->toContain("this.\$wire.upload('avatar', compressed")
+ ->toContain('canvas.toBlob')
+ ->toContain('wire:click="uploadAvatar"')
+ ->and($menu)
+ ->toContain("route('profile.avatar',");
+});
+
+it('offers runtime local or existing S3 profile picture storage', function () {
+ $component = file_get_contents(app_path('Livewire/Settings/Advanced.php'));
+ $view = file_get_contents(resource_path('views/livewire/settings/advanced.blade.php'));
+
+ expect($component)
+ ->toContain("['value' => 'local', 'label' => 'Local storage']")
+ ->toContain("'value' => 's3:'.\$storage->id")
+ ->toContain('->whereTeamId(0)')
+ ->toContain("->where('is_usable', true)")
+ ->and($view)
+ ->toContain('id="avatar_storage"')
+ ->toContain('Use S3 for multi-instance or cloud deployments');
+});
diff --git a/tests/Feature/RealtimeTerminalPackagingTest.php b/tests/Feature/RealtimeTerminalPackagingTest.php
index 2295531cc..1de4296aa 100644
--- a/tests/Feature/RealtimeTerminalPackagingTest.php
+++ b/tests/Feature/RealtimeTerminalPackagingTest.php
@@ -144,6 +144,17 @@ it('shows connection progress in the terminal body instead of the header', funct
->toContain("this.starting = this.\$el.dataset.autoStart === 'true';");
});
+it('starts the global terminal only once when a target is selected', function () {
+ $view = file_get_contents(resource_path('views/livewire/terminal/index.blade.php'));
+ $component = file_get_contents(app_path('Livewire/Terminal/Index.php'));
+
+ expect($view)
+ ->toContain("await \$wire.set('selected_uuid', target.value);")
+ ->not->toContain('await $wire.connectToContainer();')
+ ->and($component)
+ ->toMatch('/public function updatedSelectedUuid\(\).*?\$this->connectToContainer\(\);/s');
+});
+
it('uses the redesigned terminal canvas and controls on resource terminal pages', function () {
$view = file_get_contents(resource_path('views/livewire/project/shared/execute-container-command.blade.php'));
diff --git a/tests/Feature/SentinelUnsavedBarFlashTest.php b/tests/Feature/SentinelUnsavedBarFlashTest.php
index 7a545f92c..e19cfef92 100644
--- a/tests/Feature/SentinelUnsavedBarFlashTest.php
+++ b/tests/Feature/SentinelUnsavedBarFlashTest.php
@@ -108,7 +108,7 @@ test('settings advanced unsaved bar scopes dirty tracking away from instantSave
expect($contents)
->toContain('x-unsaved-bar')
- ->toContain('targets="custom_dns_servers,allowed_ips,webhook_allowed_internal_hosts,webhook_allow_localhost"')
+ ->toContain('targets="custom_dns_servers,allowed_ips,webhook_allowed_internal_hosts,webhook_allow_localhost,domain_connect_private_key"')
->toContain('onChange="instantSave"')
->not->toMatch('/x-unsaved-bar\s+action="submit"\s*\/>/');
});
diff --git a/tests/Feature/TerminalPageHeaderTest.php b/tests/Feature/TerminalPageHeaderTest.php
index e9fdb88a3..d4bfd925a 100644
--- a/tests/Feature/TerminalPageHeaderTest.php
+++ b/tests/Feature/TerminalPageHeaderTest.php
@@ -23,6 +23,15 @@ it('shows a centered themed target canvas before loading xterm', function () {
->toContain("@else\n
toContain('data-terminal-target-picker="page"')
+ ->toContain('flex max-h-full w-full max-w-lg flex-col')
+ ->toContain('class="terminal-target-list min-h-0 flex-1 overflow-y-auto p-2"');
+});
+
it('loads targets inside the themed session picker with an accent scrollbar', function () {
$view = file_get_contents(resource_path('views/livewire/terminal/index.blade.php'));
$styles = file_get_contents(resource_path('css/app.css'));
diff --git a/tests/Feature/TopUserMenuTest.php b/tests/Feature/TopUserMenuTest.php
index 16488f58d..04aef2415 100644
--- a/tests/Feature/TopUserMenuTest.php
+++ b/tests/Feature/TopUserMenuTest.php
@@ -25,8 +25,15 @@ it('changes appearance from a submenu instead of navigating to a separate page',
expect($menu)
->toContain('appearanceOpen: false')
->toContain('@click.outside="open = false; appearanceOpen = false"')
- ->toContain("theme: localStorage.getItem('theme') || 'dark'")
- ->toContain('setTheme(type)')
+ ->toContain("theme: localStorage.getItem('theme') === 'purple' ? 'custom' : (localStorage.getItem('theme') || 'dark')")
+ ->toContain('setTheme(type, closeMenu = true)')
+ ->toContain("this.setTheme('custom', false)")
+ ->not->toContain('@change="appearanceOpen = false; open = false"')
+ ->toContain('this.appearanceOpen = false;')
+ ->toContain('this.open = false;')
+ ->toContain('')
+ ->not->toContain('x-show.important="open"')
+ ->not->toContain('