Merge remote-tracking branch 'upstream/next' into feature/glitchtip-service

# Conflicts:
#	README.md
#	apps/api/src/routes/api/v1/services/handlers.ts
#	apps/ui/src/lib/components/svg/services/index.ts
This commit is contained in:
Guillaume Bonnet
2022-08-16 20:21:44 +02:00
40 changed files with 681 additions and 1073 deletions

232
README.md
View File

@@ -1,7 +1,109 @@
# Coolify # Coolify
An open-source & self-hostable Heroku / Netlify alternative An open-source & self-hostable Heroku / Netlify alternative.
(ARM support is in beta).
## Live Demo
https://demo.coolify.io/
(If it is unresponsive, that means someone overloaded the server. 😄)
## Feedback
If you have a new service / build pack you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
---
## How to install
For more details goto the [docs](https://docs.coollabs.io/coolify/installation.html).
Installation is automated with the following command:
```bash
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh
```
If you would like no questions during installation:
```bash
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -f
```
---
## Features
### Git Sources
Self-hosted versions also!
<a href="https://github.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/github.com"></a>
<a href="https://gitlab.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/gitlab.com"></a>
### Destinations
Deploy your resource to:
- Local Docker Engine
- Remote Docker Engine
### Applications
<a href="https://heroku.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/heroku.com"></a>
<a href="https://html5.org/">
<svg style="width:40px;height:40px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" ><g clip-path="url(#HTML5_Clip0_4)" ><path d="M30.216 0L27.6454 28.7967L16.0907 32L4.56783 28.8012L2 0H30.216Z" fill="#E44D26" /><path d="M16.108 29.5515L25.4447 26.963L27.6415 2.35497H16.108V29.5515Z" fill="#F16529" /><path d="M11.1109 9.4197H16.108V5.88731H7.25053L7.33509 6.83499L8.20327 16.5692H16.108V13.0369H11.4338L11.1109 9.4197Z" fill="#EBEBEB" /><path d="M11.907 18.3354H8.36111L8.856 23.8818L16.0917 25.8904L16.108 25.8859V22.2108L16.0925 22.2149L12.1585 21.1527L11.907 18.3354Z" fill="#EBEBEB" /><path d="M16.0958 16.5692H20.4455L20.0354 21.1504L16.0958 22.2138V25.8887L23.3373 23.8817L23.3904 23.285L24.2205 13.9855L24.3067 13.0369H16.0958V16.5692Z" fill="white" /><path d="M16.0958 9.41105V9.41969H24.6281L24.6989 8.62572L24.8599 6.83499L24.9444 5.88731H16.0958V9.41105Z" fill="white" /></g><defs><clipPath id="HTML5_Clip0_4"><rect width="32" height="32" fill="white" /></clipPath></defs></svg></a>
<a href="https://nodejs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nodejs.org"></a>
<a href="https://vuejs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/vuejs.org"></a>
<a href="https://nuxtjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nuxtjs.org"></a>
<a href="https://nextjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nextjs.org"></a>
<a href="https://reactjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/reactjs.org"></a>
<a href="https://preactjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/preactjs.org"></a>
<a href="https://gatsbyjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/gatsbyjs.org"></a>
<a href="https://svelte.dev"><img style="width:40px;height:40px" src="https://icon.horse/icon/svelte.dev"></a>
<a href="https://php.net"><img style="width:40px;height:40px" src="https://icon.horse/icon/php.net"></a>
<a href="https://laravel.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/laravel.com"></a>
<a href="https://python.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/python.org"></a>
<a href="https://deno.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/deno.com"></a>
<a href="https://docker.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/docker.com"></a>
### Databases
<a href="https://mongodb.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/mongodb.org"></a>
<a href="https://mariadb.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/mariadb.org"></a>
<a href="https://mysql.com"><svg style="width:40px;height:40px" xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 25.6 25.6" ><path d="M179.076 94.886c-3.568-.1-6.336.268-8.656 1.25-.668.27-1.74.27-1.828 1.116.357.355.4.936.713 1.428.535.893 1.473 2.096 2.32 2.72l2.855 2.053c1.74 1.07 3.703 1.695 5.398 2.766.982.625 1.963 1.428 2.945 2.098.5.357.803.938 1.428 1.16v-.135c-.312-.4-.402-.98-.713-1.428l-1.34-1.293c-1.293-1.74-2.9-3.258-4.64-4.506-1.428-.982-4.55-2.32-5.13-3.97l-.088-.1c.98-.1 2.14-.447 3.078-.715 1.518-.4 2.9-.312 4.46-.713l2.143-.625v-.4c-.803-.803-1.383-1.874-2.23-2.632-2.275-1.963-4.775-3.882-7.363-5.488-1.383-.892-3.168-1.473-4.64-2.23-.537-.268-1.428-.402-1.74-.848-.805-.98-1.25-2.275-1.83-3.436l-3.658-7.763c-.803-1.74-1.295-3.48-2.275-5.086-4.596-7.585-9.594-12.18-17.268-16.687-1.65-.937-3.613-1.34-5.7-1.83l-3.346-.18c-.715-.312-1.428-1.16-2.053-1.562-2.543-1.606-9.102-5.086-10.977-.5-1.205 2.9 1.785 5.755 2.8 7.228.76 1.026 1.74 2.186 2.277 3.346.3.758.4 1.562.713 2.365.713 1.963 1.383 4.15 2.32 5.98.5.937 1.025 1.92 1.65 2.767.357.5.982.714 1.115 1.517-.625.893-.668 2.23-1.025 3.347-1.607 5.042-.982 11.288 1.293 15 .715 1.115 2.4 3.57 4.686 2.632 2.008-.803 1.56-3.346 2.14-5.577.135-.535.045-.892.312-1.25v.1l1.83 3.703c1.383 2.186 3.793 4.462 5.8 5.98 1.07.803 1.918 2.187 3.256 2.677v-.135h-.088c-.268-.4-.67-.58-1.027-.892-.803-.803-1.695-1.785-2.32-2.677-1.873-2.498-3.523-5.265-4.996-8.12-.715-1.383-1.34-2.9-1.918-4.283-.27-.536-.27-1.34-.715-1.606-.67.98-1.65 1.83-2.143 3.034-.848 1.918-.936 4.283-1.248 6.737-.18.045-.1 0-.18.1-1.426-.356-1.918-1.83-2.453-3.078-1.338-3.168-1.562-8.254-.402-11.913.312-.937 1.652-3.882 1.117-4.774-.27-.848-1.16-1.338-1.652-2.008-.58-.848-1.203-1.918-1.605-2.855-1.07-2.5-1.605-5.265-2.766-7.764-.537-1.16-1.473-2.365-2.232-3.435-.848-1.205-1.783-2.053-2.453-3.48-.223-.5-.535-1.294-.178-1.83.088-.357.268-.5.623-.58.58-.5 2.232.134 2.812.4 1.65.67 3.033 1.294 4.416 2.23.625.446 1.295 1.294 2.098 1.518h.938c1.428.312 3.033.1 4.37.5 2.365.76 4.506 1.874 6.426 3.08 5.844 3.703 10.664 8.968 13.92 15.26.535 1.026.758 1.963 1.25 3.034.938 2.187 2.098 4.417 3.033 6.56.938 2.097 1.83 4.24 3.168 5.98.67.937 3.346 1.427 4.55 1.918.893.4 2.275.76 3.08 1.25 1.516.937 3.033 2.008 4.46 3.034.713.534 2.945 1.65 3.078 2.54zm-45.5-38.772a7.09 7.09 0 0 0-1.828.223v.1h.088c.357.714.982 1.205 1.428 1.83l1.027 2.142.088-.1c.625-.446.938-1.16.938-2.23-.268-.312-.312-.625-.535-.937-.268-.446-.848-.67-1.206-1.026z" transform="matrix(.390229 0 0 .38781 -46.300037 -16.856717)" fill-rule="evenodd" fill="#00678c" /></svg></a>
<a href="https://postgresql.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/postgresql.org"></a>
<a href="https://couchdb.apache.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/couchdb.apache.org"></a>
<a href="https://redis.io"><svg style="width:40px;height:40px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><defs ><path id="a" d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" /><path id="b" d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z" /></defs ><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)" ><use fill="#a41e11" xlink:href="#a" /><path d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z" fill="#d82c20" /><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" fill="#a41e11" /><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff" ><path d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z" /><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g ><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z" fill="#ad2115" /></g ></svg ></a>
### Services
- [Appwrite](https://appwrite.io)
- [WordPress](https://docs.coollabs.io/coolify/services/wordpress)
- [Ghost](https://ghost.org)
- [Plausible Analytics](https://docs.coollabs.io/coolify/services/plausible-analytics)
- [NocoDB](https://nocodb.com)
- [VSCode Server](https://github.com/cdr/code-server)
- [MinIO](https://min.io)
- [VaultWarden](https://github.com/dani-garcia/vaultwarden)
- [LanguageTool](https://languagetool.org)
- [n8n](https://n8n.io)
- [Uptime Kuma](https://github.com/louislam/uptime-kuma)
- [MeiliSearch](https://github.com/meilisearch/meilisearch)
- [Umami](https://github.com/mikecao/umami)
- [Fider](https://fider.io)
- [Hasura](https://hasura.io)
- [GlitchTip](https://glitchtip.com)
## Migration from v1
A fresh installation is necessary. v2 and v3 are not compatible with v1.
## Support
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
- Discord: [Invitation](https://discord.gg/6rDM4fkymF)
## Financial Contributors ## Financial Contributors
@@ -24,128 +126,4 @@ Support this project with your organization. Your logo will show up here with a
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a> <a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a> <a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a> <a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a> <a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
---
## Live Demo
https://demo.coolify.io/
(If it is unresponsive, that means someone overloaded the server. 😄)
## Feedback
If you have a new service / build pack you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
---
## How to install
Installation is automated with the following command:
```bash
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh
```
If you would like no questions during installation:
```bash
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -f
```
For more details goto the [docs](https://docs.coollabs.io/coolify/installation).
---
## Features
### Git Sources
You can use the following Git Sources to be auto-deployed to your Coolify instance! (Self-hosted versions are also supported.)
<a href="https://github.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/github.com"></a>
<a href="https://gitlab.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/gitlab.com"></a>
### Destinations
You can deploy your applications to the following destinations:
- Local Docker Engine
- Remote Docker Engine
### Applications
Predefined build packs to cover the basic needs to deploy applications.
If you have an advanced use case, you can use the Docker build pack that allows you to deploy your application based on your custom Dockerfile.
<a href="https://html5.org/">
<svg style="width:40px;height:40px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" ><g clip-path="url(#HTML5_Clip0_4)" ><path d="M30.216 0L27.6454 28.7967L16.0907 32L4.56783 28.8012L2 0H30.216Z" fill="#E44D26" /><path d="M16.108 29.5515L25.4447 26.963L27.6415 2.35497H16.108V29.5515Z" fill="#F16529" /><path d="M11.1109 9.4197H16.108V5.88731H7.25053L7.33509 6.83499L8.20327 16.5692H16.108V13.0369H11.4338L11.1109 9.4197Z" fill="#EBEBEB" /><path d="M11.907 18.3354H8.36111L8.856 23.8818L16.0917 25.8904L16.108 25.8859V22.2108L16.0925 22.2149L12.1585 21.1527L11.907 18.3354Z" fill="#EBEBEB" /><path d="M16.0958 16.5692H20.4455L20.0354 21.1504L16.0958 22.2138V25.8887L23.3373 23.8817L23.3904 23.285L24.2205 13.9855L24.3067 13.0369H16.0958V16.5692Z" fill="white" /><path d="M16.0958 9.41105V9.41969H24.6281L24.6989 8.62572L24.8599 6.83499L24.9444 5.88731H16.0958V9.41105Z" fill="white" /></g><defs><clipPath id="HTML5_Clip0_4"><rect width="32" height="32" fill="white" /></clipPath></defs></svg></a>
<a href="https://nodejs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nodejs.org"></a>
<a href="https://vuejs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/vuejs.org"></a>
<a href="https://nuxtjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nuxtjs.org"></a>
<a href="https://nextjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/nextjs.org"></a>
<a href="https://reactjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/reactjs.org"></a>
<a href="https://preactjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/preactjs.org"></a>
<a href="https://gatsbyjs.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/gatsbyjs.org"></a>
<a href="https://svelte.dev"><img style="width:40px;height:40px" src="https://icon.horse/icon/svelte.dev"></a>
<a href="https://php.net"><img style="width:40px;height:40px" src="https://icon.horse/icon/php.net"></a>
<a href="https://laravel.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/laravel.com"></a>
<a href="https://python.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/python.org"></a>
<a href="https://deno.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/deno.com"></a>
<a href="https://docker.com"><img style="width:40px;height:40px" src="https://icon.horse/icon/docker.com"></a>
If you have a new build pack you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
### Databases
One-click database is ready to be used internally or shared over the internet:
<a href="https://mongodb.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/mongodb.org"></a>
<a href="https://mariadb.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/mariadb.org"></a>
<a href="https://mysql.com"><svg style="width:40px;height:40px" xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 25.6 25.6" ><path d="M179.076 94.886c-3.568-.1-6.336.268-8.656 1.25-.668.27-1.74.27-1.828 1.116.357.355.4.936.713 1.428.535.893 1.473 2.096 2.32 2.72l2.855 2.053c1.74 1.07 3.703 1.695 5.398 2.766.982.625 1.963 1.428 2.945 2.098.5.357.803.938 1.428 1.16v-.135c-.312-.4-.402-.98-.713-1.428l-1.34-1.293c-1.293-1.74-2.9-3.258-4.64-4.506-1.428-.982-4.55-2.32-5.13-3.97l-.088-.1c.98-.1 2.14-.447 3.078-.715 1.518-.4 2.9-.312 4.46-.713l2.143-.625v-.4c-.803-.803-1.383-1.874-2.23-2.632-2.275-1.963-4.775-3.882-7.363-5.488-1.383-.892-3.168-1.473-4.64-2.23-.537-.268-1.428-.402-1.74-.848-.805-.98-1.25-2.275-1.83-3.436l-3.658-7.763c-.803-1.74-1.295-3.48-2.275-5.086-4.596-7.585-9.594-12.18-17.268-16.687-1.65-.937-3.613-1.34-5.7-1.83l-3.346-.18c-.715-.312-1.428-1.16-2.053-1.562-2.543-1.606-9.102-5.086-10.977-.5-1.205 2.9 1.785 5.755 2.8 7.228.76 1.026 1.74 2.186 2.277 3.346.3.758.4 1.562.713 2.365.713 1.963 1.383 4.15 2.32 5.98.5.937 1.025 1.92 1.65 2.767.357.5.982.714 1.115 1.517-.625.893-.668 2.23-1.025 3.347-1.607 5.042-.982 11.288 1.293 15 .715 1.115 2.4 3.57 4.686 2.632 2.008-.803 1.56-3.346 2.14-5.577.135-.535.045-.892.312-1.25v.1l1.83 3.703c1.383 2.186 3.793 4.462 5.8 5.98 1.07.803 1.918 2.187 3.256 2.677v-.135h-.088c-.268-.4-.67-.58-1.027-.892-.803-.803-1.695-1.785-2.32-2.677-1.873-2.498-3.523-5.265-4.996-8.12-.715-1.383-1.34-2.9-1.918-4.283-.27-.536-.27-1.34-.715-1.606-.67.98-1.65 1.83-2.143 3.034-.848 1.918-.936 4.283-1.248 6.737-.18.045-.1 0-.18.1-1.426-.356-1.918-1.83-2.453-3.078-1.338-3.168-1.562-8.254-.402-11.913.312-.937 1.652-3.882 1.117-4.774-.27-.848-1.16-1.338-1.652-2.008-.58-.848-1.203-1.918-1.605-2.855-1.07-2.5-1.605-5.265-2.766-7.764-.537-1.16-1.473-2.365-2.232-3.435-.848-1.205-1.783-2.053-2.453-3.48-.223-.5-.535-1.294-.178-1.83.088-.357.268-.5.623-.58.58-.5 2.232.134 2.812.4 1.65.67 3.033 1.294 4.416 2.23.625.446 1.295 1.294 2.098 1.518h.938c1.428.312 3.033.1 4.37.5 2.365.76 4.506 1.874 6.426 3.08 5.844 3.703 10.664 8.968 13.92 15.26.535 1.026.758 1.963 1.25 3.034.938 2.187 2.098 4.417 3.033 6.56.938 2.097 1.83 4.24 3.168 5.98.67.937 3.346 1.427 4.55 1.918.893.4 2.275.76 3.08 1.25 1.516.937 3.033 2.008 4.46 3.034.713.534 2.945 1.65 3.078 2.54zm-45.5-38.772a7.09 7.09 0 0 0-1.828.223v.1h.088c.357.714.982 1.205 1.428 1.83l1.027 2.142.088-.1c.625-.446.938-1.16.938-2.23-.268-.312-.312-.625-.535-.937-.268-.446-.848-.67-1.206-1.026z" transform="matrix(.390229 0 0 .38781 -46.300037 -16.856717)" fill-rule="evenodd" fill="#00678c" /></svg></a>
<a href="https://postgresql.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/postgresql.org"></a>
<a href="https://couchdb.apache.org"><img style="width:40px;height:40px" src="https://icon.horse/icon/couchdb.apache.org"></a>
<a href="https://redis.io"><svg style="width:40px;height:40px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ><defs ><path id="a" d="m45.536 38.764c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.813s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" /><path id="b" d="m45.536 28.733c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.935c2.332-.837 3.14-.867 5.126-.14s12.35 4.853 14.312 5.57 2.037 1.31.024 2.36z" /></defs ><g transform="matrix(.848327 0 0 .848327 -7.883573 -9.449691)" ><use fill="#a41e11" xlink:href="#a" /><path d="m45.536 34.95c-2.013 1.05-12.44 5.337-14.66 6.494s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276-2.04-1.613-.077-2.382l15.332-5.936c2.332-.836 3.14-.867 5.126-.14s12.35 4.852 14.31 5.582 2.037 1.31.024 2.36z" fill="#d82c20" /><use fill="#a41e11" xlink:href="#a" y="-6.218" /><use fill="#d82c20" xlink:href="#b" /><path d="m45.536 26.098c-2.013 1.05-12.44 5.337-14.66 6.495s-3.453 1.146-5.207.308-12.85-5.32-14.85-6.276c-1-.478-1.524-.88-1.524-1.26v-3.815s14.447-3.145 16.78-3.982 3.14-.867 5.126-.14 13.853 2.868 15.814 3.587v3.76c0 .377-.452.8-1.477 1.324z" fill="#a41e11" /><use fill="#d82c20" xlink:href="#b" y="-6.449" /><g fill="#fff" ><path d="m29.096 20.712-1.182-1.965-3.774-.34 2.816-1.016-.845-1.56 2.636 1.03 2.486-.814-.672 1.612 2.534.95-3.268.34zm-6.296 3.912 8.74-1.342-2.64 3.872z" /><ellipse cx="20.444" cy="21.402" rx="4.672" ry="1.811" /></g ><path d="m42.132 21.138-5.17 2.042-.004-4.087z" fill="#7a0c00" /><path d="m36.963 23.18-.56.22-5.166-2.042 5.723-2.264z" fill="#ad2115" /></g ></svg ></a>
If you have a new database you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
### Services
You quickly need to host a self-hostable, open-source service? You can do it with a few clicks!
- [WordPress](https://docs.coollabs.io/coolify/services/wordpress)
- [Ghost](https://ghost.org)
- [Plausible Analytics](https://docs.coollabs.io/coolify/services/plausible-analytics)
- [NocoDB](https://nocodb.com)
- [VSCode Server](https://github.com/cdr/code-server)
- [MinIO](https://min.io)
- [VaultWarden](https://github.com/dani-garcia/vaultwarden)
- [LanguageTool](https://languagetool.org)
- [n8n](https://n8n.io)
- [Uptime Kuma](https://github.com/louislam/uptime-kuma)
- [MeiliSearch](https://github.com/meilisearch/meilisearch)
- [Umami](https://github.com/mikecao/umami)
- [Fider](https://fider.io)
- [Hasura](https://hasura.io)
- [GlitchTip](https://glitchtip.com)
If you have a new service you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community!
## Migration from v1
A fresh installation is necessary. v2 and v3 are not compatible with v1.
## Support
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
- Discord: [Invitation](https://discord.gg/xhBCC7eGKw)
## License
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Please see the [LICENSE](/LICENSE) file in our repository for the full text.

View File

@@ -0,0 +1,20 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_ApplicationSettings" (
"id" TEXT NOT NULL PRIMARY KEY,
"applicationId" TEXT NOT NULL,
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
"debug" BOOLEAN NOT NULL DEFAULT false,
"previews" BOOLEAN NOT NULL DEFAULT false,
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
"isBot" BOOLEAN NOT NULL DEFAULT false,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings";
DROP TABLE "ApplicationSettings";
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@@ -124,6 +124,7 @@ model ApplicationSettings {
debug Boolean @default(false) debug Boolean @default(false)
previews Boolean @default(false) previews Boolean @default(false)
autodeploy Boolean @default(true) autodeploy Boolean @default(true)
isBot Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
application Application @relation(fields: [applicationId], references: [id]) application Application @relation(fields: [applicationId], references: [id])

View File

@@ -152,6 +152,13 @@ import * as buildpacks from '../lib/buildPacks';
.createHash('sha256') .createHash('sha256')
.update( .update(
JSON.stringify({ JSON.stringify({
pythonWSGI,
pythonModule,
pythonVariable,
deploymentType,
denoOptions,
baseImage,
baseBuildImage,
buildPack, buildPack,
port, port,
exposePort, exposePort,

View File

@@ -252,6 +252,20 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
label: 'python:3.7-slim-bullseye' label: 'python:3.7-slim-bullseye'
} }
]; ];
const herokuVersions = [
{
value: 'heroku/builder:22',
label: 'heroku/builder:22'
},
{
value: 'heroku/buildpacks:20',
label: 'heroku/buildpacks:20'
},
{
value: 'heroku/builder-classic:22',
label: 'heroku/builder-classic:22'
},
]
let payload: any = { let payload: any = {
baseImage: null, baseImage: null,
baseBuildImage: null, baseBuildImage: null,
@@ -299,6 +313,11 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
payload.baseBuildImage = 'node:18'; payload.baseBuildImage = 'node:18';
payload.baseBuildImages = nodeVersions; payload.baseBuildImages = nodeVersions;
} }
if (buildPack === 'heroku') {
payload.baseImage = 'heroku/buildpacks:20';
payload.baseImages = herokuVersions;
}
return payload; return payload;
} }

View File

@@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs'; import { day } from './dayjs';
import * as serviceFields from './serviceFields' import * as serviceFields from './serviceFields'
export const version = '3.4.0'; export const version = '3.5.0';
export const isDev = process.env.NODE_ENV === 'development'; export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr'; const algorithm = 'aes-256-ctr';
@@ -261,8 +261,8 @@ export const supportedServiceTypesAndVersions = [
fancyName: 'Hasura', fancyName: 'Hasura',
baseImage: 'hasura/graphql-engine', baseImage: 'hasura/graphql-engine',
images: ['postgres:12-alpine'], images: ['postgres:12-alpine'],
versions: ['latest', 'v2.8.4', 'v2.5.1'], versions: ['latest', 'v2.10.0', 'v2.5.1'],
recommendedVersion: 'v2.8.4', recommendedVersion: 'v2.10.0',
ports: { ports: {
main: 8080 main: 8080
} }

View File

@@ -16,7 +16,6 @@ export function formatLabelsOnDocker(data) {
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<boolean> { export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<boolean> {
let containerFound = false; let containerFound = false;
try { try {
console.log('checking ', container)
const { stdout } = await executeDockerCmd({ const { stdout } = await executeDockerCmd({
dockerId, dockerId,
command: command:

View File

@@ -13,13 +13,13 @@ export async function defaultServiceConfigurations({ id, teamId }) {
let secrets = []; let secrets = [];
if (serviceSecret.length > 0) { if (serviceSecret.length > 0) {
serviceSecret.forEach((secret) => { serviceSecret.forEach((secret) => {
secrets.push([secret.name]=secret.value); secrets.push(`${secret.name}=${secret.value}`);
}); });
} }
return { ...service, network, port, workdir, image, secrets } return { ...service, network, port, workdir, image, secrets }
} }
export function defaultServiceComposeConfiguration(network: string) { export function defaultServiceComposeConfiguration(network: string): any {
return { return {
networks: [network], networks: [network],
restart: 'always', restart: 'always',

View File

@@ -5,7 +5,7 @@ import axios from 'axios';
import { FastifyReply } from 'fastify'; import { FastifyReply } from 'fastify';
import { day } from '../../../../lib/dayjs'; import { day } from '../../../../lib/dayjs';
import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common'; import { setDefaultBaseImage, setDefaultConfiguration } from '../../../../lib/buildPacks/common';
import { checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, prisma, stopBuild, uniqueName } from '../../../../lib/common'; import { checkDomainsIsValidInDNS, checkDoubleBranch, decrypt, encrypt, errorHandler, executeDockerCmd, generateSshKeyPair, getContainerUsage, getDomain, getFreeExposedPort, isDev, isDomainConfigured, listSettings, prisma, stopBuild, uniqueName } from '../../../../lib/common';
import { checkContainer, formatLabelsOnDocker, isContainerExited, removeContainer } from '../../../../lib/docker'; import { checkContainer, formatLabelsOnDocker, isContainerExited, removeContainer } from '../../../../lib/docker';
import { scheduler } from '../../../../lib/scheduler'; import { scheduler } from '../../../../lib/scheduler';
@@ -90,10 +90,11 @@ export async function getApplication(request: FastifyRequest<OnlyId>) {
const { teamId } = request.user const { teamId } = request.user
const appId = process.env['COOLIFY_APP_ID']; const appId = process.env['COOLIFY_APP_ID'];
const application: any = await getApplicationFromDB(id, teamId); const application: any = await getApplicationFromDB(id, teamId);
const settings = await listSettings();
return { return {
application, application,
appId appId,
settings
}; };
} catch ({ status, message }) { } catch ({ status, message }) {
@@ -275,7 +276,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) { export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) {
try { try {
const { id } = request.params const { id } = request.params
const { debug, previews, dualCerts, autodeploy, branch, projectId } = request.body const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot } = request.body
const isDouble = await checkDoubleBranch(branch, projectId); const isDouble = await checkDoubleBranch(branch, projectId);
if (isDouble && autodeploy) { if (isDouble && autodeploy) {
await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } }) await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
@@ -283,7 +284,7 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
} }
await prisma.application.update({ await prisma.application.update({
where: { id }, where: { id },
data: { settings: { update: { debug, previews, dualCerts, autodeploy } } }, data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot } } },
include: { destinationDocker: true } include: { destinationDocker: true }
}); });
return reply.code(201).send(); return reply.code(201).send();

View File

@@ -25,7 +25,7 @@ export interface SaveApplication extends OnlyId {
} }
export interface SaveApplicationSettings extends OnlyId { export interface SaveApplicationSettings extends OnlyId {
Querystring: { domain: string; }; Querystring: { domain: string; };
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; }; Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; };
} }
export interface DeleteApplication extends OnlyId { export interface DeleteApplication extends OnlyId {
Querystring: { domain: string; }; Querystring: { domain: string; };

View File

@@ -3,8 +3,7 @@ import type { FastifyRequest } from 'fastify';
import { FastifyReply } from 'fastify'; import { FastifyReply } from 'fastify';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import fs from 'fs/promises'; import fs from 'fs/promises';
import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, isARM, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common'; import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, executeDockerCmd, generateDatabaseConfiguration, generatePassword, getContainerUsage, getDatabaseImage, getDatabaseVersions, getFreePublicPort, listSettings, makeLabelForStandaloneDatabase, prisma, startTraefikTCPProxy, stopDatabaseContainer, stopTcpHttpProxy, supportedDatabaseTypesAndVersions, uniqueName, updatePasswordInDb } from '../../../../lib/common';
import { checkContainer } from '../../../../lib/docker';
import { day } from '../../../../lib/dayjs'; import { day } from '../../../../lib/dayjs';
import { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types'; import { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';

File diff suppressed because it is too large Load Diff

View File

@@ -131,8 +131,8 @@ export const supportedServiceTypesAndVersions = [
fancyName: 'Hasura', fancyName: 'Hasura',
baseImage: 'hasura/graphql-engine', baseImage: 'hasura/graphql-engine',
images: ['postgres:12-alpine'], images: ['postgres:12-alpine'],
versions: ['latest', 'v2.5.1'], versions: ['latest', 'v2.10.0', 'v2.5.1'],
recommendedVersion: 'v2.5.1', recommendedVersion: 'v2.10.0',
ports: { ports: {
main: 8080 main: 8080
} }
@@ -240,7 +240,7 @@ export const staticDeployments = [
'astro', 'astro',
'eleventy' 'eleventy'
]; ];
export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel']; export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel', 'heroku'];
export function generateRemoteEngine(destination: any) { export function generateRemoteEngine(destination: any) {

View File

@@ -5,16 +5,17 @@
</script> </script>
<div <div
on:click={() => dispatch('click')}
on:mouseover={() => dispatch('pause')} on:mouseover={() => dispatch('pause')}
on:focus={() => dispatch('pause')} on:focus={() => dispatch('pause')}
on:mouseout={() => dispatch('resume')} on:mouseout={() => dispatch('resume')}
on:blur={() => dispatch('resume')} on:blur={() => dispatch('resume')}
class="alert shadow-lg text-white rounded" class="alert shadow-lg text-white rounded hover:scale-105 transition-all duration-100 cursor-pointer"
class:bg-coollabs={type === 'success'} class:bg-coollabs={type === 'success'}
class:alert-error={type === 'error'} class:alert-error={type === 'error'}
class:alert-info={type === 'info'} class:alert-info={type === 'info'}
> >
<!-- {#if type === 'success'} {#if type === 'success'}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="stroke-current flex-shrink-0 h-6 w-6" class="stroke-current flex-shrink-0 h-6 w-6"
@@ -53,6 +54,6 @@
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/></svg /></svg
> >
{/if} --> {/if}
<slot /> <slot />
</div> </div>

View File

@@ -2,7 +2,7 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import Toast from './Toast.svelte'; import Toast from './Toast.svelte';
import { pauseToast, resumeToast, toasts } from '$lib/store'; import { dismissToast, pauseToast, resumeToast, toasts } from '$lib/store';
</script> </script>
{#if $toasts} {#if $toasts}
@@ -12,7 +12,8 @@
<Toast <Toast
type={toast.type} type={toast.type}
on:resume={() => resumeToast(toast.id)} on:resume={() => resumeToast(toast.id)}
on:pause={() => pauseToast(toast.id)}>{@html toast.message}</Toast on:pause={() => pauseToast(toast.id)}
on:click={() => dismissToast(toast.id)}>{@html toast.message}</Toast
> >
{/each} {/each}
</article> </article>

View File

@@ -38,4 +38,6 @@
<Icons.Deno {isAbsolute} /> <Icons.Deno {isAbsolute} />
{:else if application.buildPack?.toLowerCase() === 'laravel'} {:else if application.buildPack?.toLowerCase() === 'laravel'}
<Icons.Laravel {isAbsolute} /> <Icons.Laravel {isAbsolute} />
{:else if application.buildPack?.toLowerCase() === 'heroku'}
<Icons.Heroku {isAbsolute} />
{/if} {/if}

View File

@@ -0,0 +1,15 @@
<script lang="ts">
export let isAbsolute = true;
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8 '}
viewBox="0 0 72 80"
>
<path
xmlns="http://www.w3.org/2000/svg"
fill="#430098"
d="M64.8,0 L7.2,0 C3.224,0 0,3.224 0,7.2 L0,72.8 C0,76.776 3.224,80 7.2,80 L64.8,80 C68.776,80 72,76.776 72,72.8 L72,7.2 C72,3.224 68.776,0 64.8,0 Z M18,68 L18,52 L27,60 L18,68 Z M46,68 L46,44.11 C45.961,42.243 45.062,40 41,40 C32.866,40 23.742,44.091 23.651,44.132 L18,46.692 L18,12 L26,12 L26,34.711 C29.994,33.411 35.577,32 41,32 C45.945,32 48.905,33.944 50.517,35.575 C53.958,39.055 54.005,43.488 54.0002258,44 L54.0002258,68 L46,68 Z M48,25 L40,25 C43.144,20.875 45.118,16.534 46,12 L54,12 C53.46,16.544 51.618,20.9 48,25 Z"
/>
</svg>

View File

@@ -16,4 +16,4 @@ export { default as Astro } from './Astro.svelte';
export { default as Eleventy } from './Eleventy.svelte'; export { default as Eleventy } from './Eleventy.svelte';
export { default as Deno } from './Deno.svelte'; export { default as Deno } from './Deno.svelte';
export { default as Laravel } from './Laravel.svelte'; export { default as Laravel } from './Laravel.svelte';
export { default as Heroku } from './Heroku.svelte';

File diff suppressed because one or more lines are too long

View File

@@ -32,6 +32,8 @@
<Icons.Hasura {isAbsolute} /> <Icons.Hasura {isAbsolute} />
{:else if type === 'fider'} {:else if type === 'fider'}
<Icons.Fider {isAbsolute} /> <Icons.Fider {isAbsolute} />
{:else if type === 'appwrite'}
<Icons.Appwrite {isAbsolute} />
{:else if type === 'moodle'} {:else if type === 'moodle'}
<Icons.Moodle {isAbsolute} /> <Icons.Moodle {isAbsolute} />
{:else if type === 'glitchTip'} {:else if type === 'glitchTip'}

View File

@@ -1,18 +1,18 @@
//@ts-nocheck //@ts-nocheck
export { default as PlausibleAnalytics } from './PlausibleAnalytics.svelte'; export { default as PlausibleAnalytics } from './PlausibleAnalytics.svelte';
export { default as NocoDb } from './NocoDB.svelte'; export { default as NocoDb } from './NocoDB.svelte';
export { default as MinIo } from './MinIO.svelte'; export { default as MinIo } from './MinIO.svelte';
export { default as VsCodeServer } from './VSCodeServer.svelte'; export { default as VsCodeServer } from './VSCodeServer.svelte';
export { default as Wordpress } from './Wordpress.svelte'; export { default as Wordpress } from './Wordpress.svelte';
export { default as VaultWarden } from './VaultWarden.svelte'; export { default as VaultWarden } from './VaultWarden.svelte';
export { default as LanguageTool } from './LanguageTool.svelte'; export { default as LanguageTool } from './LanguageTool.svelte';
export { default as N8n } from './N8n.svelte'; export { default as N8n } from './N8n.svelte';
export { default as UptimeKuma } from './UptimeKuma.svelte'; export { default as UptimeKuma } from './UptimeKuma.svelte';
export { default as Ghost } from './Ghost.svelte'; export { default as Ghost } from './Ghost.svelte';
export { default as MeiliSearch } from './MeiliSearch.svelte'; export { default as MeiliSearch } from './MeiliSearch.svelte';
export { default as Umami } from './Umami.svelte'; export { default as Umami } from './Umami.svelte';
export { default as Hasura } from './Hasura.svelte'; export { default as Hasura } from './Hasura.svelte';
export { default as Fider } from './Fider.svelte'; export { default as Fider } from './Fider.svelte';
export { default as Appwrite } from './Moodle.svelte'; export { default as Appwrite } from './Appwrite.svelte';
export { default as Moodle } from './Moodle.svelte'; export { default as Moodle } from './Moodle.svelte';
export { default as GlitchTip } from './GlitchTip.svelte'; export { default as GlitchTip } from './GlitchTip.svelte';

View File

@@ -144,8 +144,8 @@
}, },
"preview": { "preview": {
"need_during_buildtime": "Need during buildtime?", "need_during_buildtime": "Need during buildtime?",
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-applications font-bold'>staging</span> environments.", "setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-applications font-bold'>staging</span> environments.", "values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
"redeploy": "Redeploy", "redeploy": "Redeploy",
"no_previews_available": "No previews available" "no_previews_available": "No previews available"
}, },
@@ -194,14 +194,14 @@
"application": "Application", "application": "Application",
"url_fqdn": "URL (FQDN)", "url_fqdn": "URL (FQDN)",
"domain_fqdn": "Domain (FQDN)", "domain_fqdn": "Domain (FQDN)",
"https_explainer": "If you specify <span class='text-applications font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-applications font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>", "https_explainer": "If you specify <span class='text-green-500 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-green-500 font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
"ssl_www_and_non_www": "Generate SSL for www and non-www?", "ssl_www_and_non_www": "Generate SSL for www and non-www?",
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-applications'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.", "ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-green-500'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
"install_command": "Install Command", "install_command": "Install Command",
"build_command": "Build Command", "build_command": "Build Command",
"start_command": "Start Command", "start_command": "Start Command",
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-applications font-bold'>monorepos</span>.", "directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-green-500 font-bold'>monorepos</span>.",
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> or <span class='text-applications font-bold'>public</span>.", "publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> or <span class='text-green-500 font-bold'>public</span>.",
"features": "Features", "features": "Features",
"enable_automatic_deployment": "Enable Automatic Deployment", "enable_automatic_deployment": "Enable Automatic Deployment",
"enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.", "enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.",

View File

@@ -65,7 +65,7 @@
"features": "Caractéristiques", "features": "Caractéristiques",
"git_repository": "Dépôt Git", "git_repository": "Dépôt Git",
"git_source": "Source Git", "git_source": "Source Git",
"https_explainer": "Si vous spécifiez <span class='text-applications font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-applications font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>", "https_explainer": "Si vous spécifiez <span class='text-green-500 font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-green-500 font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
"install_command": "Commande d'installation", "install_command": "Commande d'installation",
"logs": "Journaux des applications", "logs": "Journaux des applications",
"no_applications_found": "Aucune application trouvée", "no_applications_found": "Aucune application trouvée",
@@ -78,11 +78,11 @@
"need_during_buildtime": "Besoin pendant la build ?", "need_during_buildtime": "Besoin pendant la build ?",
"no_previews_available": "Aucun aperçu disponible", "no_previews_available": "Aucun aperçu disponible",
"redeploy": "Redéployer", "redeploy": "Redéployer",
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>.", "setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>.",
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>." "values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>."
}, },
"previews": "Aperçus", "previews": "Aperçus",
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> ou <span \nclass='text-applications font-bold'>public</span>.", "publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> ou <span \nclass='text-green-500 font-bold'>public</span>.",
"rebuild_application": "Re-build l'application", "rebuild_application": "Re-build l'application",
"secret": "secrets", "secret": "secrets",
"secrets": { "secrets": {
@@ -91,7 +91,7 @@
"use_isbuildsecret": "Utiliser isBuildSecret" "use_isbuildsecret": "Utiliser isBuildSecret"
}, },
"settings_saved": "Paramètres sauvegardés.", "settings_saved": "Paramètres sauvegardés.",
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-applications'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.", "ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-green-500'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
"ssl_www_and_non_www": "Générer SSL pour www et non-www ?", "ssl_www_and_non_www": "Générer SSL pour www et non-www ?",
"start_command": "Démarrer la commande", "start_command": "Démarrer la commande",
"stop_application": "Arrêter l'application", "stop_application": "Arrêter l'application",

View File

@@ -70,7 +70,10 @@ export const features = readable({
}); });
export const location: Writable<null | string> = writable(null) export const location: Writable<null | string> = writable(null)
export const setLocation = (resource: any) => { export const setLocation = (resource: any, settings?: any) => {
if (resource.settings.isBot) {
return location.set(`http://${settings.ipv4}:${resource.exposePort}`)
}
if (GITPOD_WORKSPACE_URL && resource.exposePort) { if (GITPOD_WORKSPACE_URL && resource.exposePort) {
const { href } = new URL(GITPOD_WORKSPACE_URL); const { href } = new URL(GITPOD_WORKSPACE_URL);
const newURL = href const newURL = href

View File

@@ -170,6 +170,16 @@ export function findBuildPack(pack: string, packageManager = 'npm') {
port: 80 port: 80
}; };
} }
if (pack === 'heroku') {
return {
...metaData,
installCommand: null,
buildCommand: null,
startCommand: null,
publishDirectory: null,
port: 5000
};
}
return { return {
name: 'node', name: 'node',
fancyName: 'Node.js', fancyName: 'Node.js',
@@ -187,118 +197,137 @@ export const buildPacks = [
name: 'node', name: 'node',
fancyName: 'Node.js', fancyName: 'Node.js',
hoverColor: 'hover:bg-green-700', hoverColor: 'hover:bg-green-700',
color: 'bg-green-700' color: 'bg-green-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'static', name: 'static',
fancyName: 'Static', fancyName: 'Static',
hoverColor: 'hover:bg-orange-700', hoverColor: 'hover:bg-orange-700',
color: 'bg-orange-700' color: 'bg-orange-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'php', name: 'php',
fancyName: 'PHP', fancyName: 'PHP',
hoverColor: 'hover:bg-indigo-700', hoverColor: 'hover:bg-indigo-700',
color: 'bg-indigo-700' color: 'bg-indigo-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'laravel', name: 'laravel',
fancyName: 'Laravel', fancyName: 'Laravel',
hoverColor: 'hover:bg-indigo-700', hoverColor: 'hover:bg-indigo-700',
color: 'bg-indigo-700' color: 'bg-indigo-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'docker', name: 'docker',
fancyName: 'Docker', fancyName: 'Docker',
hoverColor: 'hover:bg-sky-700', hoverColor: 'hover:bg-sky-700',
color: 'bg-sky-700' color: 'bg-sky-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'svelte', name: 'svelte',
fancyName: 'Svelte', fancyName: 'Svelte',
hoverColor: 'hover:bg-orange-700', hoverColor: 'hover:bg-orange-700',
color: 'bg-orange-700' color: 'bg-orange-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'vuejs', name: 'vuejs',
fancyName: 'VueJS', fancyName: 'VueJS',
hoverColor: 'hover:bg-green-700', hoverColor: 'hover:bg-green-700',
color: 'bg-green-700' color: 'bg-green-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'nuxtjs', name: 'nuxtjs',
fancyName: 'NuxtJS', fancyName: 'NuxtJS',
hoverColor: 'hover:bg-green-700', hoverColor: 'hover:bg-green-700',
color: 'bg-green-700' color: 'bg-green-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'gatsby', name: 'gatsby',
fancyName: 'Gatsby', fancyName: 'Gatsby',
hoverColor: 'hover:bg-blue-700', hoverColor: 'hover:bg-blue-700',
color: 'bg-blue-700' color: 'bg-blue-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'astro', name: 'astro',
fancyName: 'Astro', fancyName: 'Astro',
hoverColor: 'hover:bg-pink-700', hoverColor: 'hover:bg-pink-700',
color: 'bg-pink-700' color: 'bg-pink-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'eleventy', name: 'eleventy',
fancyName: 'Eleventy', fancyName: 'Eleventy',
hoverColor: 'hover:bg-red-700', hoverColor: 'hover:bg-red-700',
color: 'bg-red-700' color: 'bg-red-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'react', name: 'react',
fancyName: 'React', fancyName: 'React',
hoverColor: 'hover:bg-blue-700', hoverColor: 'hover:bg-blue-700',
color: 'bg-blue-700' color: 'bg-blue-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'preact', name: 'preact',
fancyName: 'Preact', fancyName: 'Preact',
hoverColor: 'hover:bg-blue-700', hoverColor: 'hover:bg-blue-700',
color: 'bg-blue-700' color: 'bg-blue-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'nextjs', name: 'nextjs',
fancyName: 'NextJS', fancyName: 'NextJS',
hoverColor: 'hover:bg-blue-700', hoverColor: 'hover:bg-blue-700',
color: 'bg-blue-700' color: 'bg-blue-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'nestjs', name: 'nestjs',
fancyName: 'NestJS', fancyName: 'NestJS',
hoverColor: 'hover:bg-red-700', hoverColor: 'hover:bg-red-700',
color: 'bg-red-700' color: 'bg-red-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'rust', name: 'rust',
fancyName: 'Rust', fancyName: 'Rust',
hoverColor: 'hover:bg-pink-700', hoverColor: 'hover:bg-pink-700',
color: 'bg-pink-700' color: 'bg-pink-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'python', name: 'python',
fancyName: 'Python', fancyName: 'Python',
hoverColor: 'hover:bg-green-700', hoverColor: 'hover:bg-green-700',
color: 'bg-green-700' color: 'bg-green-700',
isCoolifyBuildPack: true,
}, },
{ {
name: 'deno', name: 'deno',
fancyName: 'Deno', fancyName: 'Deno',
hoverColor: 'hover:bg-green-700', hoverColor: 'hover:bg-green-700',
color: 'bg-green-700' color: 'bg-green-700',
isCoolifyBuildPack: true,
}, },
// { {
// name: 'heroku', name: 'heroku',
// fancyName: 'Heroku Buildpack', fancyName: 'Heroku',
// hoverColor: 'hover:bg-indigo-700', hoverColor: 'hover:bg-purple-700',
// color: 'bg-indigo-700' color: 'bg-purple-700',
// } isHerokuBuildPack: true,
}
]; ];
export const scanningTemplates = { export const scanningTemplates = {
'@sveltejs/kit': { '@sveltejs/kit': {

View File

@@ -16,7 +16,7 @@
export const load: Load = async ({ fetch, url, params }) => { export const load: Load = async ({ fetch, url, params }) => {
try { try {
const response = await get(`/applications/${params.id}`); const response = await get(`/applications/${params.id}`);
let { application, appId, settings, isQueueActive } = response; let { application, appId, settings } = response;
if (!application || Object.entries(application).length === 0) { if (!application || Object.entries(application).length === 0) {
return { return {
status: 302, status: 302,
@@ -36,7 +36,8 @@
return { return {
props: { props: {
application application,
settings
}, },
stuff: { stuff: {
application, application,
@@ -52,7 +53,7 @@
<script lang="ts"> <script lang="ts">
export let application: any; export let application: any;
export let settings: any;
import { page } from '$app/stores'; import { page } from '$app/stores';
import DeleteIcon from '$lib/components/DeleteIcon.svelte'; import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { del, get, post } from '$lib/api'; import { del, get, post } from '$lib/api';
@@ -65,10 +66,10 @@
let loading = false; let loading = false;
let statusInterval: any; let statusInterval: any;
let isQueueActive= false; let isQueueActive = false;
$disabledButton = $disabledButton =
!$appSession.isAdmin || !$appSession.isAdmin ||
!application.fqdn || (!application.fqdn && !application.settings.isBot) ||
!application.gitSource || !application.gitSource ||
!application.repository || !application.repository ||
!application.destinationDocker || !application.destinationDocker ||
@@ -80,9 +81,9 @@
try { try {
const { buildId } = await post(`/applications/${id}/deploy`, { ...application }); const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
addToast({ addToast({
message: $t('application.deployment_queued'), message: $t('application.deployment_queued'),
type: 'success' type: 'success'
}); });
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) { if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`); return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
} else { } else {
@@ -114,7 +115,7 @@
return window.location.reload(); return window.location.reload();
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} }
} }
async function getStatus() { async function getStatus() {
if ($status.application.loading) return; if ($status.application.loading) return;
@@ -126,18 +127,23 @@
$status.application.loading = false; $status.application.loading = false;
$status.application.initialLoading = false; $status.application.initialLoading = false;
} }
onDestroy(() => { onDestroy(() => {
$status.application.initialLoading = true; $status.application.initialLoading = true;
$location = null; $location = null;
clearInterval(statusInterval); clearInterval(statusInterval);
}); });
onMount(async () => { onMount(async () => {
setLocation(application); setLocation(application, settings);
$status.application.isRunning = false; $status.application.isRunning = false;
$status.application.isExited = false; $status.application.isExited = false;
$status.application.loading = false; $status.application.loading = false;
if (application.gitSourceId && application.destinationDockerId && application.fqdn) { if (
application.gitSourceId &&
application.destinationDockerId &&
application.fqdn &&
!application.settings.isBot
) {
await getStatus(); await getStatus();
statusInterval = setInterval(async () => { statusInterval = setInterval(async () => {
await getStatus(); await getStatus();

View File

@@ -30,7 +30,6 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import { get } from '$lib/api'; import { get } from '$lib/api';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import { browser } from '$app/env';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { buildPacks, findBuildPack, scanningTemplates } from '$lib/templates'; import { buildPacks, findBuildPack, scanningTemplates } from '$lib/templates';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
@@ -263,11 +262,27 @@
</div> </div>
</div> </div>
{:else} {:else}
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
{#each buildPacks as buildPack}
<div class="max-w-7xl mx-auto ">
<div class="title pb-2">Coolify Buildpacks</div>
<div class="flex flex-wrap justify-center">
{#each buildPacks.filter(bp => bp.isCoolifyBuildPack === true) as buildPack}
<div class="p-2">
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
</div>
{/each}
</div>
</div>
<div class="max-w-7xl mx-auto ">
<div class="title pb-2">Heroku</div>
<div class="flex flex-wrap justify-center">
{#each buildPacks.filter(bp => bp.isHerokuBuildPack === true) as buildPack}
<div class="p-2"> <div class="p-2">
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig /> <BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
</div> </div>
{/each} {/each}
</div> </div>
</div>
{/if} {/if}

View File

@@ -31,6 +31,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params; const { id } = $page.params;
const from = $page.url.searchParams.get('from'); const from = $page.url.searchParams.get('from');
@@ -55,6 +56,11 @@
return errorNotification(error); return errorNotification(error);
} }
} }
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
</script> </script>
<div class="flex space-x-1 p-6 font-bold"> <div class="flex space-x-1 p-6 font-bold">
@@ -65,7 +71,9 @@
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">
{#if !destinations || ownDestinations.length === 0} {#if !destinations || ownDestinations.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div> <div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center"> <div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500"> <a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg <svg

View File

@@ -5,7 +5,8 @@
if (stuff?.application?.id) { if (stuff?.application?.id) {
return { return {
props: { props: {
application: stuff.application application: stuff.application,
settings: stuff.settings
} }
}; };
} }
@@ -26,6 +27,7 @@
<script lang="ts"> <script lang="ts">
export let application: any; export let application: any;
export let settings: any;
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import Select from 'svelte-select'; import Select from 'svelte-select';
@@ -60,6 +62,7 @@
let previews = application.settings.previews; let previews = application.settings.previews;
let dualCerts = application.settings.dualCerts; let dualCerts = application.settings.dualCerts;
let autodeploy = application.settings.autodeploy; let autodeploy = application.settings.autodeploy;
let isBot = application.settings.isBot;
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, ''); let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
let isNonWWWDomainOK = false; let isNonWWWDomainOK = false;
@@ -99,7 +102,7 @@
application.fqdn = `http://${cuid()}.demo.coolify.io`; application.fqdn = `http://${cuid()}.demo.coolify.io`;
await handleSubmit(); await handleSubmit();
} }
domainEl.focus(); // !isBot && domainEl.focus();
await getUsage(); await getUsage();
usageInterval = setInterval(async () => { usageInterval = setInterval(async () => {
await getUsage(); await getUsage();
@@ -129,11 +132,16 @@
if (name === 'autodeploy') { if (name === 'autodeploy') {
autodeploy = !autodeploy; autodeploy = !autodeploy;
} }
if (name === 'isBot') {
isBot = !isBot;
setLocation(application, settings);
}
try { try {
await post(`/applications/${id}/settings`, { await post(`/applications/${id}/settings`, {
previews, previews,
debug, debug,
dualCerts, dualCerts,
isBot,
autodeploy, autodeploy,
branch: application.branch, branch: application.branch,
projectId: application.projectId projectId: application.projectId
@@ -155,24 +163,28 @@
if (name === 'autodeploy') { if (name === 'autodeploy') {
autodeploy = !autodeploy; autodeploy = !autodeploy;
} }
if (name === 'isBot') {
isBot = !isBot;
}
return errorNotification(error); return errorNotification(error);
} }
} }
async function handleSubmit() { async function handleSubmit() {
if (loading || !application.fqdn) return; if (loading || (!application.fqdn && !isBot)) return;
loading = true; loading = true;
try { try {
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, ''); nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
if (application.deploymentType) if (application.deploymentType)
application.deploymentType = application.deploymentType.toLowerCase(); application.deploymentType = application.deploymentType.toLowerCase();
await post(`/applications/${id}/check`, { !isBot &&
fqdn: application.fqdn, (await post(`/applications/${id}/check`, {
forceSave, fqdn: application.fqdn,
dualCerts, forceSave,
exposePort: application.exposePort dualCerts,
}); exposePort: application.exposePort
}));
await post(`/applications/${id}`, { ...application }); await post(`/applications/${id}`, { ...application });
setLocation(application); setLocation(application, settings);
$disabledButton = false; $disabledButton = false;
forceSave = false; forceSave = false;
addToast({ addToast({
@@ -332,53 +344,56 @@
<label for="gitSource" class="text-base font-bold text-stone-100" <label for="gitSource" class="text-base font-bold text-stone-100"
>{$t('application.git_source')}</label >{$t('application.git_source')}</label
> >
<a {#if isDisabled}
href={!isDisabled <input disabled={isDisabled} value={application.gitSource.name} />
? `/applications/${id}/configuration/source?from=/applications/${id}` {:else}
: ''} <a
class="no-underline" href={`/applications/${id}/configuration/source?from=/applications/${id}`}
><input class="no-underline"
value={application.gitSource.name} ><input
id="gitSource" value={application.gitSource.name}
disabled id="gitSource"
class="cursor-pointer hover:bg-coolgray-500" class="cursor-pointer hover:bg-coolgray-500"
/></a /></a
> >
{/if}
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="repository" class="text-base font-bold text-stone-100" <label for="repository" class="text-base font-bold text-stone-100"
>{$t('application.git_repository')}</label >{$t('application.git_repository')}</label
> >
<a {#if isDisabled}
href={!isDisabled <input disabled={isDisabled} value="{application.repository}/{application.branch}" />
? `/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack` {:else}
: ''} <a
class="no-underline" href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
><input class="no-underline"
value="{application.repository}/{application.branch}" ><input
id="repository" value="{application.repository}/{application.branch}"
disabled id="repository"
class="cursor-pointer hover:bg-coolgray-500" class="cursor-pointer hover:bg-coolgray-500"
/></a /></a
> >
{/if}
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="buildPack" class="text-base font-bold text-stone-100" <label for="buildPack" class="text-base font-bold text-stone-100"
>{$t('application.build_pack')}</label >{$t('application.build_pack')}</label
> >
<a {#if isDisabled}
href={!isDisabled <input class="capitalize" disabled={isDisabled} value={application.buildPack} />
? `/applications/${id}/configuration/buildpack?from=/applications/${id}` {:else}
: ''} <a
class="no-underline " href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
> class="no-underline "
<input >
value={application.buildPack} <input
id="buildPack" value={application.buildPack}
disabled id="buildPack"
class="cursor-pointer hover:bg-coolgray-500" class="cursor-pointer hover:bg-coolgray-500 capitalize"
/></a /></a
> >
{/if}
</div> </div>
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center pb-8">
<label for="destination" class="text-base font-bold text-stone-100" <label for="destination" class="text-base font-bold text-stone-100"
@@ -465,77 +480,88 @@
<div class="title">{$t('application.application')}</div> <div class="title">{$t('application.application')}</div>
</div> </div>
<div class="grid grid-flow-row gap-2 px-10"> <div class="grid grid-flow-row gap-2 px-10">
<div class="grid grid-cols-2"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <Setting
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100" isCenter={false}
>{$t('application.url_fqdn')}</label bind:setting={isBot}
> on:click={() => changeSettings('isBot')}
{#if browser && window.location.hostname === 'demo.coolify.io'} title="Is your application a bot?"
<Explainer description="You can deploy applications without domains. <br>They will listen on <span class='text-green-500 font-bold'>IP:PORT</span> instead.<br></Setting><br>Useful for <span class='text-green-500 font-bold'>example bots.</span>"
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>" />
</div>
{#if !isBot}
<div class="grid grid-cols-2">
<div class="flex-col">
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')}</label
>
{#if browser && window.location.hostname === 'demo.coolify.io'}
<Explainer
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
/>
{/if}
<Explainer text={$t('application.https_explainer')} />
</div>
<div>
<input
readonly={isDisabled}
disabled={isDisabled}
bind:this={domainEl}
name="fqdn"
id="fqdn"
required
bind:value={application.fqdn}
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
placeholder="eg: https://coollabs.io"
/> />
{/if} {#if forceSave}
<Explainer text={$t('application.https_explainer')} /> <div class="flex-col space-y-2 pt-4 text-center">
</div> {#if isNonWWWDomainOK}
<div>
<input
readonly={isDisabled}
disabled={isDisabled}
bind:this={domainEl}
name="fqdn"
id="fqdn"
required
bind:value={application.fqdn}
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
placeholder="eg: https://coollabs.io"
/>
{#if forceSave}
<div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK}
<button
class="bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{#if dualCerts}
{#if isWWWDomainOK}
<button <button
class="bg-green-600 hover:bg-green-500" class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} >DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
> >
{:else} {:else}
<button <button
class="bg-red-600 hover:bg-red-500" class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} >DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
> >
{/if} {/if}
{/if} {#if dualCerts}
</div> {#if isWWWDomainOK}
{/if} <button
class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{/if}
</div>
{/if}
</div>
</div> </div>
</div> <div class="grid grid-cols-2 items-center pb-8">
<div class="grid grid-cols-2 items-center pb-8"> <Setting
<Setting dataTooltip={$t('forms.must_be_stopped_to_modify')}
dataTooltip={$t('forms.must_be_stopped_to_modify')} disabled={$status.application.isRunning}
disabled={$status.application.isRunning} isCenter={false}
isCenter={false} bind:setting={dualCerts}
bind:setting={dualCerts} title={$t('application.ssl_www_and_non_www')}
title={$t('application.ssl_www_and_non_www')} description={$t('application.ssl_explainer')}
description={$t('application.ssl_explainer')} on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')} />
/> </div>
</div> {/if}
{#if application.buildPack === 'python'} {#if application.buildPack === 'python'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label> <label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label>
@@ -585,7 +611,7 @@
</div> </div>
{/if} {/if}
{/if} {/if}
{#if !staticDeployments.includes(application.buildPack)} {#if !staticDeployments.includes(application.buildPack) && !isBot}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label> <label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
<input <input
@@ -606,6 +632,7 @@
name="exposePort" name="exposePort"
id="exposePort" id="exposePort"
bind:value={application.exposePort} bind:value={application.exposePort}
required={isBot}
placeholder="12345" placeholder="12345"
/> />
<Explainer <Explainer
@@ -698,7 +725,7 @@
/> />
</div> </div>
{/if} {/if}
{#if application.buildPack !== 'laravel'} {#if application.buildPack !== 'laravel' && application.buildPack !== 'heroku'}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <div class="flex-col">
<label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100" <label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100"

View File

@@ -30,6 +30,7 @@
import { get, post } from '$lib/api'; import { get, post } from '$lib/api';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params; const { id } = $page.params;
const from = $page.url.searchParams.get('from'); const from = $page.url.searchParams.get('from');
@@ -45,6 +46,11 @@
return errorNotification(error); return errorNotification(error);
} }
} }
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
</script> </script>
<div class="flex space-x-1 p-6 font-bold"> <div class="flex space-x-1 p-6 font-bold">
@@ -55,7 +61,9 @@
<div class="flex justify-center"> <div class="flex justify-center">
{#if !destinations || destinations.length === 0} {#if !destinations || destinations.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div> <div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center"> <div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500"> <a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg <svg

View File

@@ -39,7 +39,7 @@
export let services: any; export let services: any;
let numberOfGetStatus = 0; let numberOfGetStatus = 0;
function getRndInteger(min, max) { function getRndInteger(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) ) + min; return Math.floor(Math.random() * (max - min + 1) ) + min;
} }

View File

@@ -55,7 +55,7 @@
<a href="https://fider.io" target="_blank"> <a href="https://fider.io" target="_blank">
<Icons.Fider /> <Icons.Fider />
</a> </a>
{:else if service.type === 'appwrote'} {:else if service.type === 'appwrite'}
<a href="https://appwrite.io" target="_blank"> <a href="https://appwrite.io" target="_blank">
<Icons.Appwrite/> <Icons.Appwrite/>
</a> </a>

View File

@@ -323,13 +323,13 @@
<div class="flex-col space-y-2 pt-4 text-center"> <div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK} {#if isNonWWWDomainOK}
<button <button
class="bg-green-600 hover:bg-green-500" class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)} on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button >DNS settings for {nonWWWDomain} is OK, click to recheck.</button
> >
{:else} {:else}
<button <button
class="bg-red-600 hover:bg-red-500" class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)} on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button >DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
> >
@@ -337,13 +337,13 @@
{#if dualCerts} {#if dualCerts}
{#if isWWWDomainOK} {#if isWWWDomainOK}
<button <button
class="bg-green-600 hover:bg-green-500" class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button >DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
> >
{:else} {:else}
<button <button
class="bg-red-600 hover:bg-red-500" class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)} on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button >DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
> >

View File

@@ -22,7 +22,7 @@
function generateUrl(publicPort: any) { function generateUrl(publicPort: any) {
return browser return browser
? `sftp://${ ? `sftp://${
settings.fqdn ? getDomain(settings.fqdn) : window.location.hostname settings?.fqdn ? getDomain(settings.fqdn) : window.location.hostname
}:${publicPort}` }:${publicPort}`
: 'Loading...'; : 'Loading...';
} }

View File

@@ -110,7 +110,6 @@
loading = true; loading = true;
try { try {
await post(`/services/${service.id}/${service.type}/start`, {}); await post(`/services/${service.id}/${service.type}/start`, {});
return window.location.reload();
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
} finally { } finally {

View File

@@ -32,10 +32,16 @@
import { get, post } from '$lib/api'; import { get, post } from '$lib/api';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params; const { id } = $page.params;
const from = $page.url.searchParams.get('from'); const from = $page.url.searchParams.get('from');
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
async function handleSubmit(destinationId: any) { async function handleSubmit(destinationId: any) {
try { try {
await post(`/services/${id}/configuration/destination`, { destinationId }); await post(`/services/${id}/configuration/destination`, { destinationId });
@@ -54,7 +60,9 @@
<div class="flex justify-center"> <div class="flex justify-center">
{#if !destinations || destinations.length === 0} {#if !destinations || destinations.length === 0}
<div class="flex-col"> <div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div> <div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center"> <div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500"> <a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg <svg

View File

@@ -32,12 +32,20 @@
import { get, post } from '$lib/api'; import { get, post } from '$lib/api';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification, supportedServiceTypesAndVersions } from '$lib/common'; import { errorNotification, supportedServiceTypesAndVersions } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params; const { id } = $page.params;
const from = $page.url.searchParams.get('from'); const from = $page.url.searchParams.get('from');
let recommendedVersion = supportedServiceTypesAndVersions.find( let recommendedVersion = supportedServiceTypesAndVersions.find(
({ name }) => name === type ({ name }) => name === type
)?.recommendedVersion; )?.recommendedVersion;
onMount(async () => {
if (versions.length === 1) {
await handleSubmit(versions[0]);
}
});
async function handleSubmit(version: any) { async function handleSubmit(version: any) {
try { try {
await post(`/services/${id}/configuration/version`, { version }); await post(`/services/${id}/configuration/version`, { version });

View File

@@ -102,7 +102,7 @@
{#each otherServices as service} {#each otherServices as service}
<a href="/services/{service.id}" class="p-2 no-underline"> <a href="/services/{service.id}" class="p-2 no-underline">
<div class="box-selection group relative hover:bg-pink-600"> <div class="box-selection group relative hover:bg-pink-600">
<Services type={service.type} /> <ServiceIcons type={service.type} />
<div class="truncate text-center text-xl font-bold"> <div class="truncate text-center text-xl font-bold">
{service.name} {service.name}
</div> </div>

View File

@@ -46,7 +46,7 @@ textarea {
} }
#svelte .custom-select-wrapper .selectContainer { #svelte .custom-select-wrapper .selectContainer {
@apply h-12 w-96 rounded border-none bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm; @apply h-12 w-96 rounded border border-coolgray-300 border-dashed bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm;
} }
#svelte .listContainer { #svelte .listContainer {

View File

@@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.4.0", "version": "3.5.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": "github:coollabsio/coolify", "repository": "github:coollabsio/coolify",
"scripts": { "scripts": {