fix: remove {{port}} template variable from preview URLs (#7527)

This commit is contained in:
Andras Bacsai 2025-12-07 21:55:25 +01:00 committed by GitHub
commit a088a419db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 179 additions and 41 deletions

View file

@ -96,8 +96,7 @@ public function generate()
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);
$preview_fqdn = str_replace('{{port}}', $port, $preview_fqdn);
$preview_fqdns[] = "$schema://$preview_fqdn";
$preview_fqdns[] = "$schema://$preview_fqdn{$port}";
}
$preview_fqdn = implode(',', $preview_fqdns);

View file

@ -145,11 +145,13 @@ public function generate_preview_fqdn_compose()
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$portInt = $url->getPort();
$port = $portInt !== null ? ':'.$portInt : '';
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview_fqdn = "$schema://$preview_fqdn{$port}";
$preview_domains[] = $preview_fqdn;
}

View file

@ -1145,11 +1145,13 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
$template = $resource->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$portInt = $url->getPort();
$port = $portInt !== null ? ':'.$portInt : '';
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn";
$preview_fqdn = "$schema://$preview_fqdn{$port}";
$preview->fqdn = $preview_fqdn;
$preview->save();

View file

@ -1,7 +1,7 @@
<form wire:submit="save" class="flex items-end gap-2">
<x-forms.input helper="One domain per preview." label="Domains for {{ str($serviceName)->headline() }}" id="domain"
canGate="update" :canResource="$preview->application"></x-forms.input>
<x-forms.input helper="One domain per preview." label="Domains for {{ $serviceName }}" id="domain" canGate="update"
:canResource="$preview->application"></x-forms.input>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="generate">Generate
Domain</x-forms.button>
</form>
</form>

View file

@ -4298,23 +4298,6 @@
"minversion": "0.0.0",
"port": "4242"
},
"unsend": {
"documentation": "https://docs.unsend.dev/get-started/self-hosting?utm_source=coolify.io",
"slogan": "Unsend is an open-source alternative to Resend, Sendgrid, Mailgun and Postmark etc.",
"compose": "c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1NFUlZJQ0VfREJfUE9TVEdSRVM6LXVuc2VuZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICB2b2x1bWVzOgogICAgICAtICd1bnNlbmQtcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjcnCiAgICB2b2x1bWVzOgogICAgICAtICd1bnNlbmQtcmVkaXMtZGF0YTovZGF0YScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tbWF4bWVtb3J5LXBvbGljeScKICAgICAgLSBub2V2aWN0aW9uCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMjAKICB1bnNlbmQ6CiAgICBpbWFnZTogJ3Vuc2VuZC91bnNlbmQ6bGF0ZXN0JwogICAgZXhwb3NlOgogICAgICAtIDMwMDAKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1VOU0VORF8zMDAwCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3Jlc3FsOi8vJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AcG9zdGdyZXM6NTQzMi8ke1NFUlZJQ0VfREJfUE9TVEdSRVM6LXVuc2VuZH0nCiAgICAgIC0gJ05FWFRBVVRIX1VSTD0ke1NFUlZJQ0VfVVJMX1VOU0VORH0nCiAgICAgIC0gJ05FWFRBVVRIX1NFQ1JFVD0ke1NFUlZJQ0VfQkFTRTY0XzY0X05FWFRBVVRIU0VDUkVUfScKICAgICAgLSAnQVdTX0FDQ0VTU19LRVk9JHtBV1NfQUNDRVNTX0tFWTo/fScKICAgICAgLSAnQVdTX1NFQ1JFVF9LRVk9JHtBV1NfU0VDUkVUX0tFWTo/fScKICAgICAgLSAnQVdTX0RFRkFVTFRfUkVHSU9OPSR7QVdTX0RFRkFVTFRfUkVHSU9OOj99JwogICAgICAtICdHSVRIVUJfSUQ9JHtHSVRIVUJfSUR9JwogICAgICAtICdHSVRIVUJfU0VDUkVUPSR7R0lUSFVCX1NFQ1JFVH0nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzknCiAgICAgIC0gJ05FWFRfUFVCTElDX0lTX0NMT1VEPSR7TkVYVF9QVUJMSUNfSVNfQ0xPVUQ6LWZhbHNlfScKICAgICAgLSAnQVBJX1JBVEVfTElNSVQ9JHtBUElfUkFURV9MSU1JVDotMX0nCiAgICAgIC0gSE9TVE5BTUU9MC4wLjAuMAogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAnd2dldCAtcU8tIGh0dHA6Ly91bnNlbmQ6MzAwMCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAxMAogICAgICB0aW1lb3V0OiAycwo=",
"tags": [
"resend",
"mailer",
"marketing emails",
"transaction emails",
"self-hosting",
"postmark"
],
"category": "messaging",
"logo": "svgs/unsend.svg",
"minversion": "0.0.0",
"port": "3000"
},
"unstructured": {
"documentation": "https://github.com/Unstructured-IO/unstructured-api?tab=readme-ov-file#--general-pre-processing-pipeline-for-documents?utm_source=coolify.io",
"slogan": "Unstructured provides a platform and tools to ingest and process unstructured documents for Retrieval Augmented Generation (RAG) and model fine-tuning.",
@ -4355,6 +4338,23 @@
"minversion": "0.0.0",
"port": "3001"
},
"usesend": {
"documentation": "https://docs.usesend.com/self-hosting/overview?utm_source=coolify.io",
"slogan": "Usesend is an open-source alternative to Resend, Sendgrid, Mailgun and Postmark etc.",
"compose": "c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1NFUlZJQ0VfREJfUE9TVEdSRVM6LXVzZXNlbmR9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgdm9sdW1lczoKICAgICAgLSAndXNlc2VuZC1wb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6NycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VzZXNlbmQtcmVkaXMtZGF0YTovZGF0YScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tbWF4bWVtb3J5LXBvbGljeScKICAgICAgLSBub2V2aWN0aW9uCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMjAKICB1c2VzZW5kOgogICAgaW1hZ2U6ICd1c2VzZW5kL3VzZXNlbmQ6bGF0ZXN0JwogICAgZXhwb3NlOgogICAgICAtIDMwMDAKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1VTRVNFTkRfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtTRVJWSUNFX0RCX1BPU1RHUkVTOi11c2VzZW5kfScKICAgICAgLSAnTkVYVEFVVEhfVVJMPSR7U0VSVklDRV9VUkxfVVNFU0VORH0nCiAgICAgIC0gJ05FWFRBVVRIX1NFQ1JFVD0ke1NFUlZJQ0VfQkFTRTY0XzY0X05FWFRBVVRIU0VDUkVUfScKICAgICAgLSAnQVdTX0FDQ0VTU19LRVk9JHtBV1NfQUNDRVNTX0tFWTo/fScKICAgICAgLSAnQVdTX1NFQ1JFVF9LRVk9JHtBV1NfU0VDUkVUX0tFWTo/fScKICAgICAgLSAnQVdTX0RFRkFVTFRfUkVHSU9OPSR7QVdTX0RFRkFVTFRfUkVHSU9OOj99JwogICAgICAtICdHSVRIVUJfSUQ9JHtHSVRIVUJfSUQ6P30nCiAgICAgIC0gJ0dJVEhVQl9TRUNSRVQ9JHtHSVRIVUJfU0VDUkVUOj99JwogICAgICAtICdSRURJU19VUkw9cmVkaXM6Ly9yZWRpczo2Mzc5JwogICAgICAtICdORVhUX1BVQkxJQ19JU19DTE9VRD0ke05FWFRfUFVCTElDX0lTX0NMT1VEOi1mYWxzZX0nCiAgICAgIC0gJ0FQSV9SQVRFX0xJTUlUPSR7QVBJX1JBVEVfTElNSVQ6LTF9JwogICAgICAtIEhPU1ROQU1FPTAuMC4wLjAKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJlZGlzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3dnZXQgLXFPLSBodHRwOi8vdXNlc2VuZDozMDAwIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHRpbWVvdXQ6IDJzCg==",
"tags": [
"resend",
"mailer",
"marketing emails",
"transaction emails",
"self-hosting",
"postmark"
],
"category": "messaging",
"logo": "svgs/usesend.svg",
"minversion": "0.0.0",
"port": "3000"
},
"vaultwarden": {
"documentation": "https://github.com/dani-garcia/vaultwarden?utm_source=coolify.io",
"slogan": "Vaultwarden is a password manager that allows you to securely store and manage your passwords.",

View file

@ -4298,23 +4298,6 @@
"minversion": "0.0.0",
"port": "4242"
},
"unsend": {
"documentation": "https://docs.unsend.dev/get-started/self-hosting?utm_source=coolify.io",
"slogan": "Unsend is an open-source alternative to Resend, Sendgrid, Mailgun and Postmark etc.",
"compose": "c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1NFUlZJQ0VfREJfUE9TVEdSRVM6LXVuc2VuZH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICB2b2x1bWVzOgogICAgICAtICd1bnNlbmQtcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjcnCiAgICB2b2x1bWVzOgogICAgICAtICd1bnNlbmQtcmVkaXMtZGF0YTovZGF0YScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tbWF4bWVtb3J5LXBvbGljeScKICAgICAgLSBub2V2aWN0aW9uCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMjAKICB1bnNlbmQ6CiAgICBpbWFnZTogJ3Vuc2VuZC91bnNlbmQ6bGF0ZXN0JwogICAgZXhwb3NlOgogICAgICAtIDMwMDAKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9VTlNFTkRfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfToke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QHBvc3RncmVzOjU0MzIvJHtTRVJWSUNFX0RCX1BPU1RHUkVTOi11bnNlbmR9JwogICAgICAtICdORVhUQVVUSF9VUkw9JHtTRVJWSUNFX0ZRRE5fVU5TRU5EfScKICAgICAgLSAnTkVYVEFVVEhfU0VDUkVUPSR7U0VSVklDRV9CQVNFNjRfNjRfTkVYVEFVVEhTRUNSRVR9JwogICAgICAtICdBV1NfQUNDRVNTX0tFWT0ke0FXU19BQ0NFU1NfS0VZOj99JwogICAgICAtICdBV1NfU0VDUkVUX0tFWT0ke0FXU19TRUNSRVRfS0VZOj99JwogICAgICAtICdBV1NfREVGQVVMVF9SRUdJT049JHtBV1NfREVGQVVMVF9SRUdJT046P30nCiAgICAgIC0gJ0dJVEhVQl9JRD0ke0dJVEhVQl9JRH0nCiAgICAgIC0gJ0dJVEhVQl9TRUNSRVQ9JHtHSVRIVUJfU0VDUkVUfScKICAgICAgLSAnUkVESVNfVVJMPXJlZGlzOi8vcmVkaXM6NjM3OScKICAgICAgLSAnTkVYVF9QVUJMSUNfSVNfQ0xPVUQ9JHtORVhUX1BVQkxJQ19JU19DTE9VRDotZmFsc2V9JwogICAgICAtICdBUElfUkFURV9MSU1JVD0ke0FQSV9SQVRFX0xJTUlUOi0xfScKICAgICAgLSBIT1NUTkFNRT0wLjAuMC4wCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICByZWRpczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICd3Z2V0IC1xTy0gaHR0cDovL3Vuc2VuZDozMDAwIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHRpbWVvdXQ6IDJzCg==",
"tags": [
"resend",
"mailer",
"marketing emails",
"transaction emails",
"self-hosting",
"postmark"
],
"category": "messaging",
"logo": "svgs/unsend.svg",
"minversion": "0.0.0",
"port": "3000"
},
"unstructured": {
"documentation": "https://github.com/Unstructured-IO/unstructured-api?tab=readme-ov-file#--general-pre-processing-pipeline-for-documents?utm_source=coolify.io",
"slogan": "Unstructured provides a platform and tools to ingest and process unstructured documents for Retrieval Augmented Generation (RAG) and model fine-tuning.",
@ -4355,6 +4338,23 @@
"minversion": "0.0.0",
"port": "3001"
},
"usesend": {
"documentation": "https://docs.usesend.com/self-hosting/overview?utm_source=coolify.io",
"slogan": "Usesend is an open-source alternative to Resend, Sendgrid, Mailgun and Postmark etc.",
"compose": "c2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1NFUlZJQ0VfREJfUE9TVEdSRVM6LXVzZXNlbmR9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgdm9sdW1lczoKICAgICAgLSAndXNlc2VuZC1wb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6NycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VzZXNlbmQtcmVkaXMtZGF0YTovZGF0YScKICAgIGNvbW1hbmQ6CiAgICAgIC0gcmVkaXMtc2VydmVyCiAgICAgIC0gJy0tbWF4bWVtb3J5LXBvbGljeScKICAgICAgLSBub2V2aWN0aW9uCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBQSU5HCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMjAKICB1c2VzZW5kOgogICAgaW1hZ2U6ICd1c2VzZW5kL3VzZXNlbmQ6bGF0ZXN0JwogICAgZXhwb3NlOgogICAgICAtIDMwMDAKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9VU0VTRU5EXzMwMDAKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzcWw6Ly8ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU306JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUBwb3N0Z3Jlczo1NDMyLyR7U0VSVklDRV9EQl9QT1NUR1JFUzotdXNlc2VuZH0nCiAgICAgIC0gJ05FWFRBVVRIX1VSTD0ke1NFUlZJQ0VfRlFETl9VU0VTRU5EfScKICAgICAgLSAnTkVYVEFVVEhfU0VDUkVUPSR7U0VSVklDRV9CQVNFNjRfNjRfTkVYVEFVVEhTRUNSRVR9JwogICAgICAtICdBV1NfQUNDRVNTX0tFWT0ke0FXU19BQ0NFU1NfS0VZOj99JwogICAgICAtICdBV1NfU0VDUkVUX0tFWT0ke0FXU19TRUNSRVRfS0VZOj99JwogICAgICAtICdBV1NfREVGQVVMVF9SRUdJT049JHtBV1NfREVGQVVMVF9SRUdJT046P30nCiAgICAgIC0gJ0dJVEhVQl9JRD0ke0dJVEhVQl9JRDo/fScKICAgICAgLSAnR0lUSFVCX1NFQ1JFVD0ke0dJVEhVQl9TRUNSRVQ6P30nCiAgICAgIC0gJ1JFRElTX1VSTD1yZWRpczovL3JlZGlzOjYzNzknCiAgICAgIC0gJ05FWFRfUFVCTElDX0lTX0NMT1VEPSR7TkVYVF9QVUJMSUNfSVNfQ0xPVUQ6LWZhbHNlfScKICAgICAgLSAnQVBJX1JBVEVfTElNSVQ9JHtBUElfUkFURV9MSU1JVDotMX0nCiAgICAgIC0gSE9TVE5BTUU9MC4wLjAuMAogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcmVkaXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAnd2dldCAtcU8tIGh0dHA6Ly91c2VzZW5kOjMwMDAgfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMTAKICAgICAgdGltZW91dDogMnMK",
"tags": [
"resend",
"mailer",
"marketing emails",
"transaction emails",
"self-hosting",
"postmark"
],
"category": "messaging",
"logo": "svgs/usesend.svg",
"minversion": "0.0.0",
"port": "3000"
},
"vaultwarden": {
"documentation": "https://github.com/dani-garcia/vaultwarden?utm_source=coolify.io",
"slogan": "Vaultwarden is a password manager that allows you to securely store and manage your passwords.",

View file

@ -0,0 +1,135 @@
<?php
/**
* Unit tests for preview deployment port handling.
*
* Tests verify that preview FQDNs correctly preserve the port from the original
* domain URL, which is required for Caddy proxy labels to work correctly.
*
* @see https://github.com/coollabsio/coolify/issues/2184
*/
use Spatie\Url\Url;
it('extracts port from domain URL with custom port', function () {
$domain = 'https://example.com:3000';
$url = Url::fromString($domain);
$port = $url->getPort();
expect($port)->toBe(3000);
});
it('returns null port for domain URL without port', function () {
$domain = 'https://example.com';
$url = Url::fromString($domain);
$port = $url->getPort();
expect($port)->toBeNull();
});
it('extracts port from HTTP URL with custom port', function () {
$domain = 'http://example.com:8080';
$url = Url::fromString($domain);
$port = $url->getPort();
expect($port)->toBe(8080);
});
it('generates preview FQDN with port preserved', function () {
$domain = 'https://example.com:3000';
$url = Url::fromString($domain);
$template = '{{pr_id}}.{{domain}}';
$pullRequestId = 42;
$host = $url->getHost();
$schema = $url->getScheme();
$portInt = $url->getPort();
$port = $portInt !== null ? ':'.$portInt : '';
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn{$port}";
expect($preview_fqdn)->toBe('https://42.example.com:3000');
});
it('generates preview FQDN without port when original has no port', function () {
$domain = 'https://example.com';
$url = Url::fromString($domain);
$template = '{{pr_id}}.{{domain}}';
$pullRequestId = 42;
$host = $url->getHost();
$schema = $url->getScheme();
$portInt = $url->getPort();
$port = $portInt !== null ? ':'.$portInt : '';
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn{$port}";
expect($preview_fqdn)->toBe('https://42.example.com');
});
it('handles multiple domains with different ports', function () {
$domains = [
'https://app.example.com:3000',
'https://api.example.com:8080',
'https://web.example.com',
];
$preview_fqdns = [];
$template = 'pr-{{pr_id}}.{{domain}}';
$pullRequestId = 123;
foreach ($domains as $domain) {
$url = Url::fromString($domain);
$host = $url->getHost();
$schema = $url->getScheme();
$portInt = $url->getPort();
$port = $portInt !== null ? ':'.$portInt : '';
$preview_fqdn = str_replace('{{random}}', 'xyz', $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
$preview_fqdn = "$schema://$preview_fqdn{$port}";
$preview_fqdns[] = $preview_fqdn;
}
expect($preview_fqdns[0])->toBe('https://pr-123.app.example.com:3000');
expect($preview_fqdns[1])->toBe('https://pr-123.api.example.com:8080');
expect($preview_fqdns[2])->toBe('https://pr-123.web.example.com');
});
it('extracts all URL components correctly', function () {
$domain = 'https://app.example.com:3000/api/v1';
$url = Url::fromString($domain);
expect($url->getScheme())->toBe('https');
expect($url->getHost())->toBe('app.example.com');
expect($url->getPort())->toBe(3000);
expect($url->getPath())->toBe('/api/v1');
});
it('formats port string correctly for URL construction', function () {
// Test port formatting logic
$testCases = [
['port' => 3000, 'expected' => ':3000'],
['port' => 8080, 'expected' => ':8080'],
['port' => 80, 'expected' => ':80'],
['port' => 443, 'expected' => ':443'],
['port' => null, 'expected' => ''],
];
foreach ($testCases as $case) {
$portInt = $case['port'];
$port = $portInt !== null ? ':'.$portInt : '';
expect($port)->toBe($case['expected']);
}
});