Merge remote-tracking branch 'upstream/next' into next
This commit is contained in:
		
							
								
								
									
										47
									
								
								.github/ISSUE_TEMPLATE/--bug-report.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								.github/ISSUE_TEMPLATE/--bug-report.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					name: 🐞 Bug report
 | 
				
			||||||
 | 
					description: Create a bug report to help us improve coolify
 | 
				
			||||||
 | 
					title: "[Bug]: "
 | 
				
			||||||
 | 
					labels: [Bug]
 | 
				
			||||||
 | 
					assignees:
 | 
				
			||||||
 | 
					- andrasbacsai
 | 
				
			||||||
 | 
					- vasani-arpit
 | 
				
			||||||
 | 
					body:
 | 
				
			||||||
 | 
					- type: markdown
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    value: |
 | 
				
			||||||
 | 
					      Thanks for taking the time to fill out this bug report! Please fill the form in English
 | 
				
			||||||
 | 
					- type: checkboxes
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: Is there an existing issue for this?
 | 
				
			||||||
 | 
					    options:
 | 
				
			||||||
 | 
					    - label: I have searched the existing issues
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					- type: textarea
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: Description
 | 
				
			||||||
 | 
					    description: A concise description of what you're experiencing and what you expect.
 | 
				
			||||||
 | 
					    placeholder: |
 | 
				
			||||||
 | 
					      When I do <X>, <Y> happens and I see the error message attached below:
 | 
				
			||||||
 | 
					      ```...```
 | 
				
			||||||
 | 
					      What I expect is <Z>
 | 
				
			||||||
 | 
					  validations:
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					- type: textarea
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: Steps To Reproduce
 | 
				
			||||||
 | 
					    description: Add steps to reproduce this behaviour, include console / network logs & videos
 | 
				
			||||||
 | 
					    placeholder: |
 | 
				
			||||||
 | 
					      1. Go to '...'
 | 
				
			||||||
 | 
					      2. Click on '....'
 | 
				
			||||||
 | 
					      3. Scroll down to '....'
 | 
				
			||||||
 | 
					      4. See error
 | 
				
			||||||
 | 
					  validations:
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					- type: input
 | 
				
			||||||
 | 
					  id: version
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: Version
 | 
				
			||||||
 | 
					    description: "The version of your coolify Instance"
 | 
				
			||||||
 | 
					    placeholder: "2.5.2"
 | 
				
			||||||
 | 
					  validations:
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
							
								
								
									
										31
									
								
								.github/ISSUE_TEMPLATE/--feature-request.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								.github/ISSUE_TEMPLATE/--feature-request.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					name: 🛠️ Feature request
 | 
				
			||||||
 | 
					description: Suggest an idea to improve coolify
 | 
				
			||||||
 | 
					title: '[Feature]: '
 | 
				
			||||||
 | 
					labels: [Enhancement]
 | 
				
			||||||
 | 
					assignees:
 | 
				
			||||||
 | 
					  - andrasbacsai
 | 
				
			||||||
 | 
					  - vasani-arpit
 | 
				
			||||||
 | 
					body:
 | 
				
			||||||
 | 
					  - type: markdown
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      value: |
 | 
				
			||||||
 | 
					        Thanks for taking the time to request a feature for coolify! Please also add your request here to get feedback from the community: https://feedback.coolify.io/!
 | 
				
			||||||
 | 
					  - type: checkboxes
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Is there an existing issue for this?
 | 
				
			||||||
 | 
					      description: Please search to see if an issue related to this feature request already exists.
 | 
				
			||||||
 | 
					      options:
 | 
				
			||||||
 | 
					        - label: I have searched the existing issues
 | 
				
			||||||
 | 
					          required: true
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Summary
 | 
				
			||||||
 | 
					      description: One paragraph description of the feature.
 | 
				
			||||||
 | 
					    validations:
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					  - type: textarea
 | 
				
			||||||
 | 
					    attributes:
 | 
				
			||||||
 | 
					      label: Why should this be worked on?
 | 
				
			||||||
 | 
					      description: A concise description of the problems or use cases for this feature request.
 | 
				
			||||||
 | 
					    validations:
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
							
								
								
									
										20
									
								
								.github/ISSUE_TEMPLATE/--task.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.github/ISSUE_TEMPLATE/--task.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					name: 📝 Task
 | 
				
			||||||
 | 
					description: Create a task for the team to work on
 | 
				
			||||||
 | 
					title: "[Task]: "
 | 
				
			||||||
 | 
					labels: [Task]
 | 
				
			||||||
 | 
					body:
 | 
				
			||||||
 | 
					- type: checkboxes
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: Is there an existing issue for this?
 | 
				
			||||||
 | 
					    description: Please search to see if an issue related to this already exists.
 | 
				
			||||||
 | 
					    options:
 | 
				
			||||||
 | 
					    - label: I have searched the existing issues
 | 
				
			||||||
 | 
					      required: true
 | 
				
			||||||
 | 
					- type: textarea
 | 
				
			||||||
 | 
					  attributes:
 | 
				
			||||||
 | 
					    label: SubTasks 
 | 
				
			||||||
 | 
					    placeholder: |
 | 
				
			||||||
 | 
					      - Sub Task 1
 | 
				
			||||||
 | 
					      - Sub Task 2
 | 
				
			||||||
 | 
					  validations:
 | 
				
			||||||
 | 
					    required: false
 | 
				
			||||||
							
								
								
									
										5
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					blank_issues_enabled: true
 | 
				
			||||||
 | 
					contact_links:
 | 
				
			||||||
 | 
					  - name: 🤔 Questions and Help
 | 
				
			||||||
 | 
					    url: https://discord.com/invite/6rDM4fkymF
 | 
				
			||||||
 | 
					    about: Reach out to us on discord or our github discussions page.
 | 
				
			||||||
							
								
								
									
										39
									
								
								.github/workflows/github-actions.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								.github/workflows/github-actions.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					name: release-coolify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  release:
 | 
				
			||||||
 | 
					    types: published
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  make-it-coolifyed:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					       -
 | 
				
			||||||
 | 
					        name: Checkout
 | 
				
			||||||
 | 
					        uses: actions/checkout@v2
 | 
				
			||||||
 | 
					       - 
 | 
				
			||||||
 | 
					        name: Set up QEMU
 | 
				
			||||||
 | 
					        uses: docker/setup-qemu-action@v1
 | 
				
			||||||
 | 
					       -
 | 
				
			||||||
 | 
					        name: Set up Docker Buildx
 | 
				
			||||||
 | 
					        uses: docker/setup-buildx-action@v1
 | 
				
			||||||
 | 
					       -
 | 
				
			||||||
 | 
					        name: Login to DockerHub
 | 
				
			||||||
 | 
					        uses: docker/login-action@v1 
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
				
			||||||
 | 
					          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
				
			||||||
 | 
					       - 
 | 
				
			||||||
 | 
					         name: Get current package version
 | 
				
			||||||
 | 
					         uses: martinbeentjes/npm-get-version-action@v1.2.3
 | 
				
			||||||
 | 
					         id: package-version
 | 
				
			||||||
 | 
					       -
 | 
				
			||||||
 | 
					        name: Build and push
 | 
				
			||||||
 | 
					        uses: docker/build-push-action@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          context: .
 | 
				
			||||||
 | 
					          platforms: linux/amd64,linux/arm64
 | 
				
			||||||
 | 
					          push: true
 | 
				
			||||||
 | 
					          tags: coollabsio/coolify:latest,coollabsio/coolify:${{steps.package-version.outputs.current-version}}
 | 
				
			||||||
 | 
					          cache-from: type=registry,ref=coollabsio/coolify:buildcache
 | 
				
			||||||
 | 
					          cache-to: type=registry,ref=coollabsio/coolify:buildcache,mode=max
 | 
				
			||||||
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							@@ -8,6 +8,10 @@ https://demo.coolify.io/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
(If it is unresponsive, that means someone overloaded the server. 🙃)
 | 
					(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
 | 
					## How to install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Installation is automated with the following command:
 | 
					Installation is automated with the following command:
 | 
				
			||||||
@@ -52,18 +56,21 @@ These are the predefined build packs, but with the Docker build pack, you can ho
 | 
				
			|||||||
- NuxtJS
 | 
					- NuxtJS
 | 
				
			||||||
- NextJS
 | 
					- NextJS
 | 
				
			||||||
- React/Preact
 | 
					- React/Preact
 | 
				
			||||||
- NextJS
 | 
					 | 
				
			||||||
- Gatsby
 | 
					- Gatsby
 | 
				
			||||||
- Svelte
 | 
					- Svelte
 | 
				
			||||||
- PHP
 | 
					- PHP
 | 
				
			||||||
 | 
					- Laravel
 | 
				
			||||||
- Rust
 | 
					- Rust
 | 
				
			||||||
- Docker
 | 
					- Docker
 | 
				
			||||||
 | 
					- Python
 | 
				
			||||||
 | 
					- Deno
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Databases
 | 
					### Databases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
One-click database is ready to be used internally or shared over the internet:
 | 
					One-click database is ready to be used internally or shared over the internet:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- MongoDB
 | 
					- MongoDB
 | 
				
			||||||
 | 
					- MariaDB
 | 
				
			||||||
- MySQL
 | 
					- MySQL
 | 
				
			||||||
- PostgreSQL
 | 
					- PostgreSQL
 | 
				
			||||||
- CouchDB
 | 
					- CouchDB
 | 
				
			||||||
@@ -73,9 +80,9 @@ One-click database is ready to be used internally or shared over the internet:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
You can host cool open-source services as well:
 | 
					You can host cool open-source services as well:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [WordPress](https://wordpress.org)
 | 
					- [WordPress](https://docs.coollabs.io/coolify/services/wordpress)
 | 
				
			||||||
- [Ghost](https://ghost.org)
 | 
					- [Ghost](https://ghost.org)
 | 
				
			||||||
- [Plausible Analytics](https://plausible.io)
 | 
					- [Plausible Analytics](https://docs.coollabs.io/coolify/services/plausible-analytics)
 | 
				
			||||||
- [NocoDB](https://nocodb.com)
 | 
					- [NocoDB](https://nocodb.com)
 | 
				
			||||||
- [VSCode Server](https://github.com/cdr/code-server)
 | 
					- [VSCode Server](https://github.com/cdr/code-server)
 | 
				
			||||||
- [MinIO](https://min.io)
 | 
					- [MinIO](https://min.io)
 | 
				
			||||||
@@ -85,6 +92,8 @@ You can host cool open-source services as well:
 | 
				
			|||||||
- [Uptime Kuma](https://github.com/louislam/uptime-kuma)
 | 
					- [Uptime Kuma](https://github.com/louislam/uptime-kuma)
 | 
				
			||||||
- [MeiliSearch](https://github.com/meilisearch/meilisearch)
 | 
					- [MeiliSearch](https://github.com/meilisearch/meilisearch)
 | 
				
			||||||
- [Umami](https://github.com/mikecao/umami)
 | 
					- [Umami](https://github.com/mikecao/umami)
 | 
				
			||||||
 | 
					- [Fider](https://fider.io)
 | 
				
			||||||
 | 
					- [Hasura](https://hasura.io)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Migration from v1
 | 
					## Migration from v1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								data/traefik/docker-compose-tcp.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								data/traefik/docker-compose-tcp.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					version: '3.5'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  ${ID}:
 | 
				
			||||||
 | 
					    container_name: proxy-for-${PORT}
 | 
				
			||||||
 | 
					    image: traefik:v2.6
 | 
				
			||||||
 | 
					    command:
 | 
				
			||||||
 | 
					      - --api.insecure=true
 | 
				
			||||||
 | 
					      - --entrypoints.web.address=:${PORT}
 | 
				
			||||||
 | 
					      - --providers.docker=false
 | 
				
			||||||
 | 
					      - --providers.docker.exposedbydefault=false
 | 
				
			||||||
 | 
					      - --providers.http.endpoint=http://host.docker.internal:3000/traefik.json?id=${ID}
 | 
				
			||||||
 | 
					      - --providers.http.pollTimeout=5s
 | 
				
			||||||
 | 
					      - --log.level=error
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - '${PORT}:${PORT}'
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - ${NETWORK}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  net:
 | 
				
			||||||
 | 
					    external: false
 | 
				
			||||||
 | 
					    name: ${NETWORK}
 | 
				
			||||||
							
								
								
									
										29
									
								
								docker-compose-traefik.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docker-compose-traefik.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					version: '3.8'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  proxy:
 | 
				
			||||||
 | 
					    image: traefik:v2.6
 | 
				
			||||||
 | 
					    command:
 | 
				
			||||||
 | 
					      - --api.insecure=true
 | 
				
			||||||
 | 
					      - --entrypoints.web.address=:80
 | 
				
			||||||
 | 
					      - --entrypoints.websecure.address=:443
 | 
				
			||||||
 | 
					      - --providers.docker=false
 | 
				
			||||||
 | 
					      - --providers.docker.exposedbydefault=false
 | 
				
			||||||
 | 
					      - --providers.http.endpoint=http://host.docker.internal:3000/traefik.json
 | 
				
			||||||
 | 
					      - --providers.http.pollTimeout=5s
 | 
				
			||||||
 | 
					      - --log.level=error
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - '80:80'
 | 
				
			||||||
 | 
					      - '443:443'
 | 
				
			||||||
 | 
					      - '8080:8080'
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - /var/run/docker.sock:/var/run/docker.sock
 | 
				
			||||||
 | 
					    extra_hosts:
 | 
				
			||||||
 | 
					      - 'host.docker.internal:host-gateway'
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      - coolify-infra
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  coolify-infra:
 | 
				
			||||||
 | 
					    attachable: true
 | 
				
			||||||
 | 
					    name: coolify-infra
 | 
				
			||||||
@@ -39,3 +39,5 @@ volumes:
 | 
				
			|||||||
    name: coolify-ssl-certs
 | 
					    name: coolify-ssl-certs
 | 
				
			||||||
  coolify-letsencrypt:
 | 
					  coolify-letsencrypt:
 | 
				
			||||||
    name: coolify-letsencrypt
 | 
					    name: coolify-letsencrypt
 | 
				
			||||||
 | 
					  coolify-traefik-letsencrypt:
 | 
				
			||||||
 | 
					    name: coolify-traefik-letsencrypt
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								package.json
									
									
									
									
									
								
							@@ -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": "2.6.1",
 | 
						"version": "2.9.0",
 | 
				
			||||||
	"license": "AGPL-3.0",
 | 
						"license": "AGPL-3.0",
 | 
				
			||||||
	"scripts": {
 | 
						"scripts": {
 | 
				
			||||||
		"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
 | 
							"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",
 | 
				
			||||||
@@ -30,60 +30,63 @@
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
	"devDependencies": {
 | 
						"devDependencies": {
 | 
				
			||||||
		"@sveltejs/adapter-node": "1.0.0-next.73",
 | 
							"@sveltejs/adapter-node": "1.0.0-next.73",
 | 
				
			||||||
		"@sveltejs/kit": "1.0.0-next.316",
 | 
							"@sveltejs/adapter-static": "1.0.0-next.31",
 | 
				
			||||||
		"@types/js-cookie": "3.0.1",
 | 
							"@sveltejs/kit": "1.0.0-next.334",
 | 
				
			||||||
 | 
							"@types/js-cookie": "3.0.2",
 | 
				
			||||||
		"@types/js-yaml": "4.0.5",
 | 
							"@types/js-yaml": "4.0.5",
 | 
				
			||||||
		"@types/node": "17.0.25",
 | 
							"@types/node": "17.0.34",
 | 
				
			||||||
		"@types/node-forge": "1.0.1",
 | 
							"@types/node-forge": "1.0.2",
 | 
				
			||||||
		"@typescript-eslint/eslint-plugin": "4.31.1",
 | 
							"@typescript-eslint/eslint-plugin": "4.31.1",
 | 
				
			||||||
		"@typescript-eslint/parser": "4.31.1",
 | 
							"@typescript-eslint/parser": "4.31.1",
 | 
				
			||||||
		"@zerodevx/svelte-toast": "0.7.1",
 | 
							"@zerodevx/svelte-toast": "0.7.1",
 | 
				
			||||||
		"autoprefixer": "10.4.4",
 | 
							"autoprefixer": "10.4.7",
 | 
				
			||||||
		"cross-env": "7.0.3",
 | 
							"cross-env": "7.0.3",
 | 
				
			||||||
		"cross-var": "1.1.0",
 | 
							"cross-var": "1.1.0",
 | 
				
			||||||
		"eslint": "7.32.0",
 | 
							"eslint": "7.32.0",
 | 
				
			||||||
		"eslint-config-prettier": "8.5.0",
 | 
							"eslint-config-prettier": "8.5.0",
 | 
				
			||||||
		"eslint-plugin-svelte3": "3.4.1",
 | 
							"eslint-plugin-svelte3": "3.4.1",
 | 
				
			||||||
		"husky": "7.0.4",
 | 
							"husky": "7.0.4",
 | 
				
			||||||
		"lint-staged": "12.4.0",
 | 
							"lint-staged": "12.4.1",
 | 
				
			||||||
		"postcss": "8.4.12",
 | 
							"postcss": "8.4.13",
 | 
				
			||||||
		"prettier": "2.6.2",
 | 
							"prettier": "2.6.2",
 | 
				
			||||||
		"prettier-plugin-svelte": "2.7.0",
 | 
							"prettier-plugin-svelte": "2.7.0",
 | 
				
			||||||
		"prettier-plugin-tailwindcss": "0.1.10",
 | 
							"prettier-plugin-tailwindcss": "0.1.11",
 | 
				
			||||||
		"prisma": "3.11.1",
 | 
							"prisma": "3.11.1",
 | 
				
			||||||
		"svelte": "3.47.0",
 | 
							"svelte": "3.48.0",
 | 
				
			||||||
		"svelte-check": "2.7.0",
 | 
							"svelte-check": "2.7.1",
 | 
				
			||||||
		"svelte-preprocess": "4.10.6",
 | 
							"svelte-preprocess": "4.10.6",
 | 
				
			||||||
		"svelte-select": "4.4.7",
 | 
							"svelte-select": "4.4.7",
 | 
				
			||||||
		"sveltekit-i18n": "2.1.2",
 | 
							"sveltekit-i18n": "2.2.1",
 | 
				
			||||||
		"tailwindcss": "3.0.24",
 | 
							"tailwindcss": "3.0.24",
 | 
				
			||||||
		"ts-node": "10.7.0",
 | 
							"ts-node": "10.7.0",
 | 
				
			||||||
		"tslib": "2.3.1",
 | 
							"tslib": "2.4.0",
 | 
				
			||||||
		"typescript": "4.6.3"
 | 
							"typescript": "4.6.4"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"type": "module",
 | 
						"type": "module",
 | 
				
			||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
		"@iarna/toml": "2.2.5",
 | 
							"@iarna/toml": "2.2.5",
 | 
				
			||||||
		"@prisma/client": "3.11.1",
 | 
							"@prisma/client": "3.11.1",
 | 
				
			||||||
		"@sentry/node": "6.19.6",
 | 
							"@sentry/node": "6.19.7",
 | 
				
			||||||
		"bcryptjs": "2.4.3",
 | 
							"bcryptjs": "2.4.3",
 | 
				
			||||||
		"bullmq": "1.80.4",
 | 
							"bullmq": "1.82.2",
 | 
				
			||||||
		"compare-versions": "4.1.3",
 | 
							"compare-versions": "4.1.3",
 | 
				
			||||||
		"cookie": "0.5.0",
 | 
							"cookie": "0.5.0",
 | 
				
			||||||
		"cuid": "2.1.8",
 | 
							"cuid": "2.1.8",
 | 
				
			||||||
		"dayjs": "1.11.1",
 | 
							"dayjs": "1.11.2",
 | 
				
			||||||
		"dockerode": "3.3.1",
 | 
							"dockerode": "3.3.1",
 | 
				
			||||||
		"dotenv-extended": "2.9.0",
 | 
							"dotenv-extended": "2.9.0",
 | 
				
			||||||
		"generate-password": "1.7.0",
 | 
							"generate-password": "1.7.0",
 | 
				
			||||||
		"get-port": "6.1.2",
 | 
							"get-port": "6.1.2",
 | 
				
			||||||
		"got": "12.0.3",
 | 
							"got": "12.0.4",
 | 
				
			||||||
 | 
							"is-ip": "4.0.0",
 | 
				
			||||||
		"js-cookie": "3.0.1",
 | 
							"js-cookie": "3.0.1",
 | 
				
			||||||
		"js-yaml": "4.1.0",
 | 
							"js-yaml": "4.1.0",
 | 
				
			||||||
		"jsonwebtoken": "8.5.1",
 | 
							"jsonwebtoken": "8.5.1",
 | 
				
			||||||
		"mustache": "4.2.0",
 | 
							"mustache": "4.2.0",
 | 
				
			||||||
		"node-forge": "1.3.1",
 | 
							"node-forge": "1.3.1",
 | 
				
			||||||
 | 
							"node-os-utils": "1.3.6",
 | 
				
			||||||
		"p-limit": "4.0.0",
 | 
							"p-limit": "4.0.0",
 | 
				
			||||||
		"svelte-kit-cookie-session": "2.1.3",
 | 
							"svelte-kit-cookie-session": "2.1.4",
 | 
				
			||||||
		"tailwindcss-scrollbar": "0.1.0",
 | 
							"tailwindcss-scrollbar": "0.1.0",
 | 
				
			||||||
		"unique-names-generator": "4.7.1"
 | 
							"unique-names-generator": "4.7.1"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										434
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										434
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER;
 | 
				
			||||||
@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Service" ADD COLUMN "exposePort" INTEGER;
 | 
				
			||||||
@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					-- RedefineTables
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=OFF;
 | 
				
			||||||
 | 
					CREATE TABLE "new_PlausibleAnalytics" (
 | 
				
			||||||
 | 
					    "id" TEXT NOT NULL PRIMARY KEY,
 | 
				
			||||||
 | 
					    "email" TEXT,
 | 
				
			||||||
 | 
					    "username" TEXT,
 | 
				
			||||||
 | 
					    "password" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "postgresqlUser" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "postgresqlPassword" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "postgresqlDatabase" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "postgresqlPublicPort" INTEGER,
 | 
				
			||||||
 | 
					    "secretKeyBase" TEXT,
 | 
				
			||||||
 | 
					    "scriptName" TEXT NOT NULL DEFAULT 'plausible.js',
 | 
				
			||||||
 | 
					    "serviceId" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
 | 
					    "updatedAt" DATETIME NOT NULL,
 | 
				
			||||||
 | 
					    CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					INSERT INTO "new_PlausibleAnalytics" ("createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username") SELECT "createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username" FROM "PlausibleAnalytics";
 | 
				
			||||||
 | 
					DROP TABLE "PlausibleAnalytics";
 | 
				
			||||||
 | 
					ALTER TABLE "new_PlausibleAnalytics" RENAME TO "PlausibleAnalytics";
 | 
				
			||||||
 | 
					CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId");
 | 
				
			||||||
 | 
					PRAGMA foreign_key_check;
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=ON;
 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					-- RedefineTables
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=OFF;
 | 
				
			||||||
 | 
					CREATE TABLE "new_Wordpress" (
 | 
				
			||||||
 | 
					    "id" TEXT NOT NULL PRIMARY KEY,
 | 
				
			||||||
 | 
					    "extraConfig" TEXT,
 | 
				
			||||||
 | 
					    "tablePrefix" TEXT,
 | 
				
			||||||
 | 
					    "ownMysql" BOOLEAN NOT NULL DEFAULT false,
 | 
				
			||||||
 | 
					    "mysqlHost" TEXT,
 | 
				
			||||||
 | 
					    "mysqlPort" INTEGER,
 | 
				
			||||||
 | 
					    "mysqlUser" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "mysqlPassword" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "mysqlRootUser" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "mysqlRootUserPassword" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "mysqlDatabase" TEXT,
 | 
				
			||||||
 | 
					    "mysqlPublicPort" INTEGER,
 | 
				
			||||||
 | 
					    "ftpEnabled" BOOLEAN NOT NULL DEFAULT false,
 | 
				
			||||||
 | 
					    "ftpUser" TEXT,
 | 
				
			||||||
 | 
					    "ftpPassword" TEXT,
 | 
				
			||||||
 | 
					    "ftpPublicPort" INTEGER,
 | 
				
			||||||
 | 
					    "ftpHostKey" TEXT,
 | 
				
			||||||
 | 
					    "ftpHostKeyPrivate" TEXT,
 | 
				
			||||||
 | 
					    "serviceId" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
 | 
					    "updatedAt" DATETIME NOT NULL,
 | 
				
			||||||
 | 
					    CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress";
 | 
				
			||||||
 | 
					DROP TABLE "Wordpress";
 | 
				
			||||||
 | 
					ALTER TABLE "new_Wordpress" RENAME TO "Wordpress";
 | 
				
			||||||
 | 
					CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId");
 | 
				
			||||||
 | 
					PRAGMA foreign_key_check;
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=ON;
 | 
				
			||||||
							
								
								
									
										24
									
								
								prisma/migrations/20220517081328_traefik/migration.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								prisma/migrations/20220517081328_traefik/migration.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					-- RedefineTables
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=OFF;
 | 
				
			||||||
 | 
					CREATE TABLE "new_Setting" (
 | 
				
			||||||
 | 
					    "id" TEXT NOT NULL PRIMARY KEY,
 | 
				
			||||||
 | 
					    "fqdn" TEXT,
 | 
				
			||||||
 | 
					    "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
 | 
				
			||||||
 | 
					    "dualCerts" BOOLEAN NOT NULL DEFAULT false,
 | 
				
			||||||
 | 
					    "minPort" INTEGER NOT NULL DEFAULT 9000,
 | 
				
			||||||
 | 
					    "maxPort" INTEGER NOT NULL DEFAULT 9100,
 | 
				
			||||||
 | 
					    "proxyPassword" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "proxyUser" TEXT NOT NULL,
 | 
				
			||||||
 | 
					    "proxyHash" TEXT,
 | 
				
			||||||
 | 
					    "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
 | 
				
			||||||
 | 
					    "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
 | 
				
			||||||
 | 
					    "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
 | 
				
			||||||
 | 
					    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
				
			||||||
 | 
					    "updatedAt" DATETIME NOT NULL
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting";
 | 
				
			||||||
 | 
					DROP TABLE "Setting";
 | 
				
			||||||
 | 
					ALTER TABLE "new_Setting" RENAME TO "Setting";
 | 
				
			||||||
 | 
					CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
 | 
				
			||||||
 | 
					PRAGMA foreign_key_check;
 | 
				
			||||||
 | 
					PRAGMA foreign_keys=ON;
 | 
				
			||||||
@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Minio" ADD COLUMN "apiFqdn" TEXT;
 | 
				
			||||||
@@ -20,6 +20,7 @@ model Setting {
 | 
				
			|||||||
  proxyHash             String?
 | 
					  proxyHash             String?
 | 
				
			||||||
  isAutoUpdateEnabled   Boolean  @default(false)
 | 
					  isAutoUpdateEnabled   Boolean  @default(false)
 | 
				
			||||||
  isDNSCheckEnabled     Boolean  @default(true)
 | 
					  isDNSCheckEnabled     Boolean  @default(true)
 | 
				
			||||||
 | 
					  isTraefikUsed         Boolean  @default(true)
 | 
				
			||||||
  createdAt             DateTime @default(now())
 | 
					  createdAt             DateTime @default(now())
 | 
				
			||||||
  updatedAt             DateTime @updatedAt
 | 
					  updatedAt             DateTime @updatedAt
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -84,6 +85,7 @@ model Application {
 | 
				
			|||||||
  buildPack           String?
 | 
					  buildPack           String?
 | 
				
			||||||
  projectId           Int?
 | 
					  projectId           Int?
 | 
				
			||||||
  port                Int?
 | 
					  port                Int?
 | 
				
			||||||
 | 
					  exposePort          Int?
 | 
				
			||||||
  installCommand      String?
 | 
					  installCommand      String?
 | 
				
			||||||
  buildCommand        String?
 | 
					  buildCommand        String?
 | 
				
			||||||
  startCommand        String?
 | 
					  startCommand        String?
 | 
				
			||||||
@@ -289,6 +291,7 @@ model Service {
 | 
				
			|||||||
  id                  String                     @id @default(cuid())
 | 
					  id                  String                     @id @default(cuid())
 | 
				
			||||||
  name                String
 | 
					  name                String
 | 
				
			||||||
  fqdn                String?
 | 
					  fqdn                String?
 | 
				
			||||||
 | 
					  exposePort          Int?
 | 
				
			||||||
  dualCerts           Boolean                    @default(false)
 | 
					  dualCerts           Boolean                    @default(false)
 | 
				
			||||||
  type                String?
 | 
					  type                String?
 | 
				
			||||||
  version             String?
 | 
					  version             String?
 | 
				
			||||||
@@ -320,6 +323,7 @@ model PlausibleAnalytics {
 | 
				
			|||||||
  postgresqlDatabase   String
 | 
					  postgresqlDatabase   String
 | 
				
			||||||
  postgresqlPublicPort Int?
 | 
					  postgresqlPublicPort Int?
 | 
				
			||||||
  secretKeyBase        String?
 | 
					  secretKeyBase        String?
 | 
				
			||||||
 | 
					  scriptName           String   @default("plausible.js")
 | 
				
			||||||
  serviceId            String   @unique
 | 
					  serviceId            String   @unique
 | 
				
			||||||
  service              Service  @relation(fields: [serviceId], references: [id])
 | 
					  service              Service  @relation(fields: [serviceId], references: [id])
 | 
				
			||||||
  createdAt            DateTime @default(now())
 | 
					  createdAt            DateTime @default(now())
 | 
				
			||||||
@@ -331,6 +335,7 @@ model Minio {
 | 
				
			|||||||
  rootUser         String
 | 
					  rootUser         String
 | 
				
			||||||
  rootUserPassword String
 | 
					  rootUserPassword String
 | 
				
			||||||
  publicPort       Int?
 | 
					  publicPort       Int?
 | 
				
			||||||
 | 
					  apiFqdn          String?
 | 
				
			||||||
  serviceId        String   @unique
 | 
					  serviceId        String   @unique
 | 
				
			||||||
  service          Service  @relation(fields: [serviceId], references: [id])
 | 
					  service          Service  @relation(fields: [serviceId], references: [id])
 | 
				
			||||||
  createdAt        DateTime @default(now())
 | 
					  createdAt        DateTime @default(now())
 | 
				
			||||||
@@ -350,6 +355,9 @@ model Wordpress {
 | 
				
			|||||||
  id                    String   @id @default(cuid())
 | 
					  id                    String   @id @default(cuid())
 | 
				
			||||||
  extraConfig           String?
 | 
					  extraConfig           String?
 | 
				
			||||||
  tablePrefix           String?
 | 
					  tablePrefix           String?
 | 
				
			||||||
 | 
					  ownMysql              Boolean  @default(false)
 | 
				
			||||||
 | 
					  mysqlHost             String?
 | 
				
			||||||
 | 
					  mysqlPort             Int?
 | 
				
			||||||
  mysqlUser             String
 | 
					  mysqlUser             String
 | 
				
			||||||
  mysqlPassword         String
 | 
					  mysqlPassword         String
 | 
				
			||||||
  mysqlRootUser         String
 | 
					  mysqlRootUser         String
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,6 @@
 | 
				
			|||||||
	<head>
 | 
						<head>
 | 
				
			||||||
		<meta charset="utf-8" />
 | 
							<meta charset="utf-8" />
 | 
				
			||||||
		<meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
							<meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
				
			||||||
		<title>Coolify</title>
 | 
					 | 
				
			||||||
		%svelte.head%
 | 
							%svelte.head%
 | 
				
			||||||
	</head>
 | 
						</head>
 | 
				
			||||||
	<body>
 | 
						<body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,5 +114,5 @@ export const getSession: GetSession = function ({ locals }) {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function handleError({ error, event }) {
 | 
					export async function handleError({ error, event }) {
 | 
				
			||||||
	if (!dev) sentry.captureException(error, event);
 | 
						// if (!dev) sentry.captureException(error, event);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,14 +8,16 @@ import { staticDeployments } from '$lib/components/common';
 | 
				
			|||||||
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
 | 
					const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
 | 
				
			||||||
const nodeBased = [
 | 
					const nodeBased = [
 | 
				
			||||||
	'react',
 | 
						'react',
 | 
				
			||||||
 | 
						'preact',
 | 
				
			||||||
	'vuejs',
 | 
						'vuejs',
 | 
				
			||||||
	'svelte',
 | 
						'svelte',
 | 
				
			||||||
	'gatsby',
 | 
						'gatsby',
 | 
				
			||||||
	'php',
 | 
					 | 
				
			||||||
	'astro',
 | 
						'astro',
 | 
				
			||||||
	'eleventy',
 | 
						'eleventy',
 | 
				
			||||||
	'node',
 | 
						'node',
 | 
				
			||||||
	'nestjs'
 | 
						'nestjs',
 | 
				
			||||||
 | 
						'nuxtjs',
 | 
				
			||||||
 | 
						'nextjs'
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function makeLabelForStandaloneApplication({
 | 
					export function makeLabelForStandaloneApplication({
 | 
				
			||||||
@@ -403,7 +405,72 @@ export function setDefaultBaseImage(buildPack) {
 | 
				
			|||||||
			label: 'webdevops/php-nginx:7.1-alpine'
 | 
								label: 'webdevops/php-nginx:7.1-alpine'
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	];
 | 
						];
 | 
				
			||||||
 | 
						const pythonVersions = [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.10-alpine',
 | 
				
			||||||
 | 
								label: 'python:3.10-alpine'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.10-buster',
 | 
				
			||||||
 | 
								label: 'python:3.10-buster'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.10-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.10-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.10-slim-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.10-slim-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.9-alpine',
 | 
				
			||||||
 | 
								label: 'python:3.9-alpine'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.9-buster',
 | 
				
			||||||
 | 
								label: 'python:3.9-buster'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.9-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.9-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.9-slim-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.9-slim-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.8-alpine',
 | 
				
			||||||
 | 
								label: 'python:3.8-alpine'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.8-buster',
 | 
				
			||||||
 | 
								label: 'python:3.8-buster'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.8-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.8-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.8-slim-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.8-slim-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.7-alpine',
 | 
				
			||||||
 | 
								label: 'python:3.7-alpine'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.7-buster',
 | 
				
			||||||
 | 
								label: 'python:3.7-buster'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.7-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.7-bullseye'
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								value: 'python:3.7-slim-bullseye',
 | 
				
			||||||
 | 
								label: 'python:3.7-slim-bullseye'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						];
 | 
				
			||||||
	let payload = {
 | 
						let payload = {
 | 
				
			||||||
		baseImage: null,
 | 
							baseImage: null,
 | 
				
			||||||
		baseBuildImage: null,
 | 
							baseBuildImage: null,
 | 
				
			||||||
@@ -423,7 +490,8 @@ export function setDefaultBaseImage(buildPack) {
 | 
				
			|||||||
		payload.baseBuildImages = nodeVersions;
 | 
							payload.baseBuildImages = nodeVersions;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (buildPack === 'python') {
 | 
						if (buildPack === 'python') {
 | 
				
			||||||
		payload.baseImage = 'python:3-alpine';
 | 
							payload.baseImage = 'python:3.10-alpine';
 | 
				
			||||||
 | 
							payload.baseImages = pythonVersions;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (buildPack === 'rust') {
 | 
						if (buildPack === 'rust') {
 | 
				
			||||||
		payload.baseImage = 'rust:latest';
 | 
							payload.baseImage = 'rust:latest';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, imageforBuild): Promise<void> => {
 | 
					const createDockerfile = async (data, imageforBuild): Promise<void> => {
 | 
				
			||||||
	const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
 | 
						const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`FROM ${imageforBuild}`);
 | 
						Dockerfile.push(`FROM ${imageforBuild}`);
 | 
				
			||||||
@@ -12,7 +12,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
 | 
				
			|||||||
	if (baseImage.includes('nginx')) {
 | 
						if (baseImage.includes('nginx')) {
 | 
				
			||||||
		Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
							Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildCacheImageForLaravel, buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, image): Promise<void> => {
 | 
					const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			||||||
	const { workdir, applicationId, tag, buildId } = data;
 | 
						const { workdir, applicationId, tag, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`FROM ${image}`);
 | 
						Dockerfile.push(`FROM ${image}`);
 | 
				
			||||||
@@ -24,7 +24,7 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
		`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json`
 | 
							`COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json`
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	Dockerfile.push(`COPY --chown=application:application . ./`);
 | 
						Dockerfile.push(`COPY --chown=application:application . ./`);
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
 | 
					const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
 | 
				
			||||||
	const { workdir, baseDirectory, buildId } = data;
 | 
						const { workdir, baseDirectory, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
	let composerFound = false;
 | 
						let composerFound = false;
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
@@ -22,7 +22,7 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
 | 
						Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`);
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, image): Promise<void> => {
 | 
					const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			||||||
	const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
 | 
						const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`FROM ${image}`);
 | 
						Dockerfile.push(`FROM ${image}`);
 | 
				
			||||||
@@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
	if (baseImage.includes('nginx')) {
 | 
						if (baseImage.includes('nginx')) {
 | 
				
			||||||
		Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
							Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,8 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
		secrets,
 | 
							secrets,
 | 
				
			||||||
		pullmergeRequestId,
 | 
							pullmergeRequestId,
 | 
				
			||||||
		baseImage,
 | 
							baseImage,
 | 
				
			||||||
		buildId
 | 
							buildId,
 | 
				
			||||||
 | 
							port
 | 
				
			||||||
	} = data;
 | 
						} = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,7 +43,7 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
	if (baseImage.includes('nginx')) {
 | 
						if (baseImage.includes('nginx')) {
 | 
				
			||||||
		Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
							Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, image): Promise<void> => {
 | 
					const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			||||||
	const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
 | 
						const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`FROM ${image}`);
 | 
						Dockerfile.push(`FROM ${image}`);
 | 
				
			||||||
@@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
	if (baseImage.includes('nginx')) {
 | 
						if (baseImage.includes('nginx')) {
 | 
				
			||||||
		Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
							Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
 | 
				
			|||||||
import { promises as fs } from 'fs';
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createDockerfile = async (data, image): Promise<void> => {
 | 
					const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			||||||
	const { applicationId, tag, workdir, publishDirectory, baseImage, buildId } = data;
 | 
						const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data;
 | 
				
			||||||
	const Dockerfile: Array<string> = [];
 | 
						const Dockerfile: Array<string> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Dockerfile.push(`FROM ${image}`);
 | 
						Dockerfile.push(`FROM ${image}`);
 | 
				
			||||||
@@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
 | 
				
			|||||||
	if (baseImage.includes('nginx')) {
 | 
						if (baseImage.includes('nginx')) {
 | 
				
			||||||
		Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
							Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	Dockerfile.push(`EXPOSE 80`);
 | 
						Dockerfile.push(`EXPOSE ${port}`);
 | 
				
			||||||
	await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
						await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ import { dev } from '$app/env';
 | 
				
			|||||||
import * as Sentry from '@sentry/node';
 | 
					import * as Sentry from '@sentry/node';
 | 
				
			||||||
import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator';
 | 
					import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator';
 | 
				
			||||||
import type { Config } from 'unique-names-generator';
 | 
					import type { Config } from 'unique-names-generator';
 | 
				
			||||||
 | 
					import { promises as dns } from 'dns';
 | 
				
			||||||
 | 
					import { isIP } from 'is-ip';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { buildLogQueue } from './queues';
 | 
					import { buildLogQueue } from './queues';
 | 
				
			||||||
@@ -14,24 +16,25 @@ import Cookie from 'cookie';
 | 
				
			|||||||
import os from 'os';
 | 
					import os from 'os';
 | 
				
			||||||
import type { RequestEvent } from '@sveltejs/kit/types/internal';
 | 
					import type { RequestEvent } from '@sveltejs/kit/types/internal';
 | 
				
			||||||
import type { Job } from 'bullmq';
 | 
					import type { Job } from 'bullmq';
 | 
				
			||||||
 | 
					import { t } from './translations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try {
 | 
					try {
 | 
				
			||||||
	if (!dev) {
 | 
						if (!dev) {
 | 
				
			||||||
		Sentry.init({
 | 
							// Sentry.init({
 | 
				
			||||||
			dsn: process.env['COOLIFY_SENTRY_DSN'],
 | 
							// 	dsn: process.env['COOLIFY_SENTRY_DSN'],
 | 
				
			||||||
			tracesSampleRate: 0,
 | 
							// 	tracesSampleRate: 0,
 | 
				
			||||||
			environment: 'production',
 | 
							// 	environment: 'production',
 | 
				
			||||||
			debug: true,
 | 
							// 	debug: true,
 | 
				
			||||||
			release: currentVersion,
 | 
							// 	release: currentVersion,
 | 
				
			||||||
			initialScope: {
 | 
							// 	initialScope: {
 | 
				
			||||||
				tags: {
 | 
							// 		tags: {
 | 
				
			||||||
					appId: process.env['COOLIFY_APP_ID'],
 | 
							// 			appId: process.env['COOLIFY_APP_ID'],
 | 
				
			||||||
					'os.arch': getOsArch(),
 | 
							// 			'os.arch': getOsArch(),
 | 
				
			||||||
					'os.platform': os.platform(),
 | 
							// 			'os.platform': os.platform(),
 | 
				
			||||||
					'os.release': os.release()
 | 
							// 			'os.release': os.release()
 | 
				
			||||||
				}
 | 
							// 		}
 | 
				
			||||||
			}
 | 
							// 	}
 | 
				
			||||||
		});
 | 
							// });
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
} catch (err) {
 | 
					} catch (err) {
 | 
				
			||||||
	console.log('Could not initialize Sentry, no worries.');
 | 
						console.log('Could not initialize Sentry, no worries.');
 | 
				
			||||||
@@ -93,12 +96,16 @@ export const getUserDetails = async (
 | 
				
			|||||||
	const userId = event?.locals?.session?.data?.userId || null;
 | 
						const userId = event?.locals?.session?.data?.userId || null;
 | 
				
			||||||
	let permission = 'read';
 | 
						let permission = 'read';
 | 
				
			||||||
	if (teamId && userId) {
 | 
						if (teamId && userId) {
 | 
				
			||||||
		const data = await db.prisma.permission.findFirst({
 | 
							try {
 | 
				
			||||||
			where: { teamId, userId },
 | 
								const data = await db.prisma.permission.findFirst({
 | 
				
			||||||
			select: { permission: true },
 | 
									where: { teamId, userId },
 | 
				
			||||||
			rejectOnNotFound: true
 | 
									select: { permission: true },
 | 
				
			||||||
		});
 | 
									rejectOnNotFound: true
 | 
				
			||||||
		if (data.permission) permission = data.permission;
 | 
								});
 | 
				
			||||||
 | 
								if (data.permission) permission = data.permission;
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								console.log(error);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const payload = {
 | 
						const payload = {
 | 
				
			||||||
@@ -179,3 +186,97 @@ export function getDomain(domain: string): string {
 | 
				
			|||||||
export function getOsArch() {
 | 
					export function getOsArch() {
 | 
				
			||||||
	return os.arch();
 | 
						return os.arch();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function isDNSValid(event: any, domain: string): Promise<any> {
 | 
				
			||||||
 | 
						let resolves = [];
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							if (isIP(event.url.hostname)) {
 | 
				
			||||||
 | 
								resolves = [event.url.hostname];
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								resolves = await dns.resolve4(event.url.hostname);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							throw {
 | 
				
			||||||
 | 
								message: t.get('application.dns_not_set_error', { domain })
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							let ipDomainFound = false;
 | 
				
			||||||
 | 
							dns.setServers(['1.1.1.1', '8.8.8.8']);
 | 
				
			||||||
 | 
							const dnsResolve = await dns.resolve4(domain);
 | 
				
			||||||
 | 
							if (dnsResolve.length > 0) {
 | 
				
			||||||
 | 
								for (const ip of dnsResolve) {
 | 
				
			||||||
 | 
									if (resolves.includes(ip)) {
 | 
				
			||||||
 | 
										ipDomainFound = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!ipDomainFound) throw false;
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							throw {
 | 
				
			||||||
 | 
								message: t.get('application.domain_not_valid')
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function checkDomainsIsValidInDNS({ event, fqdn, dualCerts }): Promise<any> {
 | 
				
			||||||
 | 
						const domain = getDomain(fqdn);
 | 
				
			||||||
 | 
						const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`;
 | 
				
			||||||
 | 
						dns.setServers(['1.1.1.1', '8.8.8.8']);
 | 
				
			||||||
 | 
						let resolves = [];
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							if (isIP(event.url.hostname)) {
 | 
				
			||||||
 | 
								resolves = [event.url.hostname];
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								resolves = await dns.resolve4(event.url.hostname);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							throw {
 | 
				
			||||||
 | 
								message: t.get('application.dns_not_set_error', { domain })
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dualCerts) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								const ipDomain = await dns.resolve4(domain);
 | 
				
			||||||
 | 
								const ipDomainDualCert = await dns.resolve4(domainDualCert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let ipDomainFound = false;
 | 
				
			||||||
 | 
								let ipDomainDualCertFound = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (const ip of ipDomain) {
 | 
				
			||||||
 | 
									if (resolves.includes(ip)) {
 | 
				
			||||||
 | 
										ipDomainFound = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for (const ip of ipDomainDualCert) {
 | 
				
			||||||
 | 
									if (resolves.includes(ip)) {
 | 
				
			||||||
 | 
										ipDomainDualCertFound = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (ipDomainFound && ipDomainDualCertFound) return { status: 200 };
 | 
				
			||||||
 | 
								throw false;
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								throw {
 | 
				
			||||||
 | 
									message: t.get('application.dns_not_set_error', { domain })
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								const ipDomain = await dns.resolve4(domain);
 | 
				
			||||||
 | 
								let ipDomainFound = false;
 | 
				
			||||||
 | 
								for (const ip of ipDomain) {
 | 
				
			||||||
 | 
									if (resolves.includes(ip)) {
 | 
				
			||||||
 | 
										ipDomainFound = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (ipDomainFound) return { status: 200 };
 | 
				
			||||||
 | 
								throw false;
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								throw {
 | 
				
			||||||
 | 
									message: t.get('application.dns_not_set_error', { domain })
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
	import Clickhouse from './svg/databases/Clickhouse.svelte';
 | 
						import Clickhouse from './svg/databases/Clickhouse.svelte';
 | 
				
			||||||
	import CouchDb from './svg/databases/CouchDB.svelte';
 | 
						import CouchDb from './svg/databases/CouchDB.svelte';
 | 
				
			||||||
	import MongoDb from './svg/databases/MongoDB.svelte';
 | 
						import MongoDb from './svg/databases/MongoDB.svelte';
 | 
				
			||||||
 | 
						import MariaDb from './svg/databases/MariaDB.svelte';
 | 
				
			||||||
	import MySql from './svg/databases/MySQL.svelte';
 | 
						import MySql from './svg/databases/MySQL.svelte';
 | 
				
			||||||
	import PostgreSql from './svg/databases/PostgreSQL.svelte';
 | 
						import PostgreSql from './svg/databases/PostgreSQL.svelte';
 | 
				
			||||||
	import Redis from './svg/databases/Redis.svelte';
 | 
						import Redis from './svg/databases/Redis.svelte';
 | 
				
			||||||
@@ -17,6 +18,8 @@
 | 
				
			|||||||
		<MongoDb />
 | 
							<MongoDb />
 | 
				
			||||||
	{:else if database.type === 'mysql'}
 | 
						{:else if database.type === 'mysql'}
 | 
				
			||||||
		<MySql />
 | 
							<MySql />
 | 
				
			||||||
 | 
						{:else if database.type === 'mariadb'}
 | 
				
			||||||
 | 
							<MariaDb />
 | 
				
			||||||
	{:else if database.type === 'postgresql'}
 | 
						{:else if database.type === 'postgresql'}
 | 
				
			||||||
		<PostgreSql />
 | 
							<PostgreSql />
 | 
				
			||||||
	{:else if database.type === 'redis'}
 | 
						{:else if database.type === 'redis'}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								src/lib/components/PageLoader.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/lib/components/PageLoader.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					<script>
 | 
				
			||||||
 | 
						import { onMount, onDestroy } from 'svelte';
 | 
				
			||||||
 | 
						import { tweened } from 'svelte/motion';
 | 
				
			||||||
 | 
						import { cubicOut } from 'svelte/easing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let timeout;
 | 
				
			||||||
 | 
						const progress = tweened(0, {
 | 
				
			||||||
 | 
							duration: 2000,
 | 
				
			||||||
 | 
							easing: cubicOut
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						onMount(() => {
 | 
				
			||||||
 | 
							timeout = setTimeout(() => {
 | 
				
			||||||
 | 
								progress.set(0.7);
 | 
				
			||||||
 | 
							}, 500);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						onDestroy(() => {
 | 
				
			||||||
 | 
							clearTimeout(timeout);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="progress-bar">
 | 
				
			||||||
 | 
						<div class="progress-sliver" style={`--width: ${$progress * 100}%`} />
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="postcss">
 | 
				
			||||||
 | 
						.progress-bar {
 | 
				
			||||||
 | 
							height: 0.2rem;
 | 
				
			||||||
 | 
							@apply fixed top-0 left-0 right-0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						.progress-sliver {
 | 
				
			||||||
 | 
							width: var(--width);
 | 
				
			||||||
 | 
							@apply h-full bg-coollabs;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -52,6 +52,12 @@ export const supportedDatabaseTypesAndVersions = [
 | 
				
			|||||||
		versions: ['5.0', '4.4', '4.2']
 | 
							versions: ['5.0', '4.4', '4.2']
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
 | 
						{ name: 'mysql', fancyName: 'MySQL', baseImage: 'bitnami/mysql', versions: ['8.0', '5.7'] },
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							name: 'mariadb',
 | 
				
			||||||
 | 
							fancyName: 'MariaDB',
 | 
				
			||||||
 | 
							baseImage: 'bitnami/mariadb',
 | 
				
			||||||
 | 
							versions: ['10.7', '10.6', '10.5', '10.4', '10.3', '10.2']
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		name: 'postgresql',
 | 
							name: 'postgresql',
 | 
				
			||||||
		fancyName: 'PostgreSQL',
 | 
							fancyName: 'PostgreSQL',
 | 
				
			||||||
@@ -213,5 +219,25 @@ export const supportedServiceTypesAndVersions = [
 | 
				
			|||||||
		ports: {
 | 
							ports: {
 | 
				
			||||||
			main: 3000
 | 
								main: 3000
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// },
 | 
				
			||||||
 | 
							// {
 | 
				
			||||||
 | 
							// 	name: 'appwrite',
 | 
				
			||||||
 | 
							// 	fancyName: 'AppWrite',
 | 
				
			||||||
 | 
							// 	baseImage: 'appwrite/appwrite',
 | 
				
			||||||
 | 
							// 	images: ['appwrite/influxdb', 'appwrite/telegraf', 'mariadb:10.7', 'redis:6.0-alpine3.12'],
 | 
				
			||||||
 | 
							// 	versions: ['latest', '0.13.0'],
 | 
				
			||||||
 | 
							// 	recommendedVersion: '0.13.0',
 | 
				
			||||||
 | 
							// 	ports: {
 | 
				
			||||||
 | 
							// 		main: 3000
 | 
				
			||||||
 | 
							// 	}
 | 
				
			||||||
 | 
							// }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getServiceMainPort = (service: string) => {
 | 
				
			||||||
 | 
						const serviceType = supportedServiceTypesAndVersions.find((s) => s.name === service);
 | 
				
			||||||
 | 
						if (serviceType) {
 | 
				
			||||||
 | 
							return serviceType.ports.main;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								src/lib/components/svg/databases/MariaDB.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/lib/components/svg/databases/MariaDB.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						export let isAbsolute = false;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<svg
 | 
				
			||||||
 | 
						xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
						id="Layer_1"
 | 
				
			||||||
 | 
						data-name="Layer 1"
 | 
				
			||||||
 | 
						viewBox="0 0 309.88 252.72"
 | 
				
			||||||
 | 
						class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12 ' : 'mx-auto w-8 h-8'}
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
						<defs>
 | 
				
			||||||
 | 
							<style>
 | 
				
			||||||
 | 
								.cls-1 {
 | 
				
			||||||
 | 
									fill: #fff;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							</style>
 | 
				
			||||||
 | 
						</defs>
 | 
				
			||||||
 | 
						<path
 | 
				
			||||||
 | 
							class="cls-1"
 | 
				
			||||||
 | 
							d="M316,10.05a4.2,4.2,0,0,0-2.84-1c-2.84,0-6.5,1.92-8.46,3l-.79.4a26.81,26.81,0,0,1-10.57,2.66c-3.76.12-7,.34-11.22.77-25,2.58-36.15,21.74-46.89,40.27-5.84,10.08-11.88,20.5-20.16,28.57a55.71,55.71,0,0,1-5.46,4.63c-8.57,6.39-19.33,10.9-27.74,14.12-8.07,3.08-16.86,5.85-25.37,8.53-7.78,2.45-15.14,4.76-21.9,7.28-3.05,1.13-5.64,2-7.93,2.76-6.15,2-10.6,3.53-17.08,8-2.53,1.73-5.07,3.6-6.8,5a71.26,71.26,0,0,0-13.54,14.27A84.81,84.81,0,0,1,77.88,163c-1.36,1.34-3.8,2-7.43,2-4.27,0-9.43-.88-14.91-1.81s-11.46-2-16.46-2c-4.07,0-7.17.66-9.5,2,0,0-3.9,2.28-5.56,5.23l1.62.73a33.56,33.56,0,0,1,6.93,5,33.68,33.68,0,0,0,7.19,5.12A6.37,6.37,0,0,1,42,180.72c-.69,1-1.69,2.29-2.74,3.67-5.77,7.55-9.13,12.32-7.2,14.92a6,6,0,0,0,3,.68c12.59,0,19.34-3.27,27.9-7.41,2.47-1.2,5-2.44,8-3.7,5-2.17,10.38-5.63,16.08-9.29,7.55-4.85,15.36-9.87,22.92-12.3a62.3,62.3,0,0,1,19.23-2.7c8,0,16.42,1.07,24.54,2.11,6.06.78,12.32,1.58,18.47,2,2.39.14,4.6.21,6.76.21a78.48,78.48,0,0,0,8.61-.45l.68-.24c4.32-2.65,6.34-8.34,8.29-13.84,1.26-3.54,2.32-6.72,4-8.74a2.06,2.06,0,0,1,.33-.27.4.4,0,0,1,.49.08.25.25,0,0,1,0,.16c-1,21.51-9.67,35.16-18.42,47.3L177,199.14s8.18,0,12.84-1.8c17-5.08,29.84-16.28,39.18-34.14a144.39,144.39,0,0,0,6.16-14.09c.16-.4,1.64-1.14,1.49.93,0,.61-.08,1.29-.13,2h0c0,.42-.06.85-.08,1.28-.25,3-1,9.34-1,9.34l5.25-2.81c12.66-8,22.42-24.14,29.82-49.25,3.09-10.46,5.34-20.85,7.33-30,2.38-11,4.43-20.43,6.78-24.09,3.69-5.74,9.32-9.62,14.77-13.39.75-.51,1.49-1,2.22-1.54,6.86-4.81,13.67-10.36,15.16-20.71l0-.23C317.93,12.92,317,11,316,10.05Z"
 | 
				
			||||||
 | 
							transform="translate(-7.45 -9.1)"
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
@@ -3,31 +3,88 @@
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<svg
 | 
					<svg
 | 
				
			||||||
	class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
 | 
						viewBox="0 0 128 128"
 | 
				
			||||||
	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'}
 | 
				
			||||||
	id="Layer_1"
 | 
					>
 | 
				
			||||||
	data-name="Layer 1"
 | 
						<path
 | 
				
			||||||
	viewBox="0 0 216.56 448.5"
 | 
							fill-rule="evenodd"
 | 
				
			||||||
	><defs
 | 
							clip-rule="evenodd"
 | 
				
			||||||
		><style>
 | 
							fill="#439934"
 | 
				
			||||||
			.cls-1 {
 | 
							d="M88.038 42.812c1.605 4.643 2.761 9.383 3.141 14.296.472 6.095.256 12.147-1.029 18.142-.035.165-.109.32-.164.48-.403.001-.814-.049-1.208.012-3.329.523-6.655 1.065-9.981 1.604-3.438.557-6.881 1.092-10.313 1.687-1.216.21-2.721-.041-3.212 1.641-.014.046-.154.054-.235.08l.166-10.051-.169-24.252 1.602-.275c2.62-.429 5.24-.864 7.862-1.281 3.129-.497 6.261-.98 9.392-1.465 1.381-.215 2.764-.412 4.148-.618z"
 | 
				
			||||||
				fill: #10aa50;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			.cls-2 {
 | 
					 | 
				
			||||||
				fill: #b8c4c2;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			.cls-3 {
 | 
					 | 
				
			||||||
				fill: #12924f;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		</style></defs
 | 
					 | 
				
			||||||
	><path
 | 
					 | 
				
			||||||
		class="cls-1"
 | 
					 | 
				
			||||||
		d="M202.8,179.68c-23-101.47-71-128.49-83.18-147.59C113,21.7,106.25,5.91,106.25,5.91c-.66,9-1.83,14.7-9.51,21.54C81.36,41.16,16,94.42,10.51,209.72c-5.12,107.5,79,173.8,90.18,180.65,8.54,4.2,19,.08,24-3.77,40.54-27.84,96-102.07,78.06-206.92"
 | 
					 | 
				
			||||||
	/><path
 | 
						/><path
 | 
				
			||||||
		class="cls-2"
 | 
							fill-rule="evenodd"
 | 
				
			||||||
		d="M109.73,333.11c-2.11,26.62-3.63,42.11-9,57.29,0,0,3.54,25.33,6,52.17l8.77,0a488.62,488.62,0,0,1,9.57-56.2C113.71,380.8,110.16,356.46,109.73,333.11Z"
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#45A538"
 | 
				
			||||||
 | 
							d="M61.729 110.054c-1.69-1.453-3.439-2.842-5.059-4.37-8.717-8.222-15.093-17.899-18.233-29.566-.865-3.211-1.442-6.474-1.627-9.792-.13-2.322-.318-4.665-.154-6.975.437-6.144 1.325-12.229 3.127-18.147l.099-.138c.175.233.427.439.516.702 1.759 5.18 3.505 10.364 5.242 15.551 5.458 16.3 10.909 32.604 16.376 48.9.107.318.384.579.583.866l-.87 2.969z"
 | 
				
			||||||
	/><path
 | 
						/><path
 | 
				
			||||||
		class="cls-3"
 | 
							fill-rule="evenodd"
 | 
				
			||||||
		d="M125.06,386.39h0c-11.48-5.3-14.8-30.13-15.31-53.28A1090.8,1090.8,0,0,0,112.2,218.4c-.6-20.07.3-185.92-4.94-210.2,2.12,4.75,7.24,15.91,12.36,23.88,12.23,19.11,60.19,46.13,83.17,147.61C220.7,284.27,165.57,358.37,125.06,386.39Z"
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#46A037"
 | 
				
			||||||
 | 
							d="M88.038 42.812c-1.384.206-2.768.403-4.149.616-3.131.485-6.263.968-9.392 1.465-2.622.417-5.242.852-7.862 1.281l-1.602.275-.012-1.045c-.053-.859-.144-1.717-.154-2.576-.069-5.478-.112-10.956-.18-16.434-.042-3.429-.105-6.857-.175-10.285-.043-2.13-.089-4.261-.185-6.388-.052-1.143-.236-2.28-.311-3.423-.042-.657.016-1.319.029-1.979.817 1.583 1.616 3.178 2.456 4.749 1.327 2.484 3.441 4.314 5.344 6.311 7.523 7.892 12.864 17.068 16.193 27.433z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#409433"
 | 
				
			||||||
 | 
							d="M65.036 80.753c.081-.026.222-.034.235-.08.491-1.682 1.996-1.431 3.212-1.641 3.432-.594 6.875-1.13 10.313-1.687 3.326-.539 6.652-1.081 9.981-1.604.394-.062.805-.011 1.208-.012-.622 2.22-1.112 4.488-1.901 6.647-.896 2.449-1.98 4.839-3.131 7.182a49.142 49.142 0 01-6.353 9.763c-1.919 2.308-4.058 4.441-6.202 6.548-1.185 1.165-2.582 2.114-3.882 3.161l-.337-.23-1.214-1.038-1.256-2.753a41.402 41.402 0 01-1.394-9.838l.023-.561.171-2.426c.057-.828.133-1.655.168-2.485.129-2.982.241-5.964.359-8.946z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#4FAA41"
 | 
				
			||||||
 | 
							d="M65.036 80.753c-.118 2.982-.23 5.964-.357 8.947-.035.83-.111 1.657-.168 2.485l-.765.289c-1.699-5.002-3.399-9.951-5.062-14.913-2.75-8.209-5.467-16.431-8.213-24.642a4498.887 4498.887 0 00-6.7-19.867c-.105-.31-.407-.552-.617-.826l4.896-9.002c.168.292.39.565.496.879a6167.476 6167.476 0 016.768 20.118c2.916 8.73 5.814 17.467 8.728 26.198.116.349.308.671.491 1.062l.67-.78-.167 10.052z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#4AA73C"
 | 
				
			||||||
 | 
							d="M43.155 32.227c.21.274.511.516.617.826a4498.887 4498.887 0 016.7 19.867c2.746 8.211 5.463 16.433 8.213 24.642 1.662 4.961 3.362 9.911 5.062 14.913l.765-.289-.171 2.426-.155.559c-.266 2.656-.49 5.318-.814 7.968-.163 1.328-.509 2.632-.772 3.947-.198-.287-.476-.548-.583-.866-5.467-16.297-10.918-32.6-16.376-48.9a3888.972 3888.972 0 00-5.242-15.551c-.089-.263-.34-.469-.516-.702l3.272-8.84z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#57AE47"
 | 
				
			||||||
 | 
							d="M65.202 70.702l-.67.78c-.183-.391-.375-.714-.491-1.062-2.913-8.731-5.812-17.468-8.728-26.198a6167.476 6167.476 0 00-6.768-20.118c-.105-.314-.327-.588-.496-.879l6.055-7.965c.191.255.463.482.562.769 1.681 4.921 3.347 9.848 5.003 14.778 1.547 4.604 3.071 9.215 4.636 13.813.105.308.47.526.714.786l.012 1.045c.058 8.082.115 16.167.171 24.251z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#60B24F"
 | 
				
			||||||
 | 
							d="M65.021 45.404c-.244-.26-.609-.478-.714-.786-1.565-4.598-3.089-9.209-4.636-13.813-1.656-4.93-3.322-9.856-5.003-14.778-.099-.287-.371-.514-.562-.769 1.969-1.928 3.877-3.925 5.925-5.764 1.821-1.634 3.285-3.386 3.352-5.968.003-.107.059-.214.145-.514l.519 1.306c-.013.661-.072 1.322-.029 1.979.075 1.143.259 2.28.311 3.423.096 2.127.142 4.258.185 6.388.069 3.428.132 6.856.175 10.285.067 5.478.111 10.956.18 16.434.008.861.098 1.718.152 2.577z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#A9AA88"
 | 
				
			||||||
 | 
							d="M62.598 107.085c.263-1.315.609-2.62.772-3.947.325-2.649.548-5.312.814-7.968l.066-.01.066.011a41.402 41.402 0 001.394 9.838c-.176.232-.425.439-.518.701-.727 2.05-1.412 4.116-2.143 6.166-.1.28-.378.498-.574.744l-.747-2.566.87-2.969z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#B6B598"
 | 
				
			||||||
 | 
							d="M62.476 112.621c.196-.246.475-.464.574-.744.731-2.05 1.417-4.115 2.143-6.166.093-.262.341-.469.518-.701l1.255 2.754c-.248.352-.59.669-.728 1.061l-2.404 7.059c-.099.283-.437.483-.663.722l-.695-3.985z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#C2C1A7"
 | 
				
			||||||
 | 
							d="M63.171 116.605c.227-.238.564-.439.663-.722l2.404-7.059c.137-.391.48-.709.728-1.061l1.215 1.037c-.587.58-.913 1.25-.717 2.097l-.369 1.208c-.168.207-.411.387-.494.624-.839 2.403-1.64 4.819-2.485 7.222-.107.305-.404.544-.614.812-.109-1.387-.22-2.771-.331-4.158z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#CECDB7"
 | 
				
			||||||
 | 
							d="M63.503 120.763c.209-.269.506-.508.614-.812.845-2.402 1.646-4.818 2.485-7.222.083-.236.325-.417.494-.624l-.509 5.545c-.136.157-.333.294-.398.477-.575 1.614-1.117 3.24-1.694 4.854-.119.333-.347.627-.525.938-.158-.207-.441-.407-.454-.623-.051-.841-.016-1.688-.013-2.533z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#DBDAC7"
 | 
				
			||||||
 | 
							d="M63.969 123.919c.178-.312.406-.606.525-.938.578-1.613 1.119-3.239 1.694-4.854.065-.183.263-.319.398-.477l.012 3.64-1.218 3.124-1.411-.495z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#EBE9DC"
 | 
				
			||||||
 | 
							d="M65.38 124.415l1.218-3.124.251 3.696-1.469-.572z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#CECDB7"
 | 
				
			||||||
 | 
							d="M67.464 110.898c-.196-.847.129-1.518.717-2.097l.337.23-1.054 1.867z"
 | 
				
			||||||
 | 
						/><path
 | 
				
			||||||
 | 
							fill-rule="evenodd"
 | 
				
			||||||
 | 
							clip-rule="evenodd"
 | 
				
			||||||
 | 
							fill="#4FAA41"
 | 
				
			||||||
 | 
							d="M64.316 95.172l-.066-.011-.066.01.155-.559-.023.56z"
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
</svg>
 | 
					</svg>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<img
 | 
					<img
 | 
				
			||||||
	alt="plausible logo"
 | 
						alt="plausible logo"
 | 
				
			||||||
	class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-6 mx-auto'}
 | 
						class={isAbsolute ? 'w-9 absolute top-0 left-0 -m-4' : 'w-6 mx-auto'}
 | 
				
			||||||
	src="/plausible.png"
 | 
						src="/plausible.png"
 | 
				
			||||||
/>
 | 
					/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,9 +128,7 @@ export function findBuildPack(pack, packageManager = 'npm') {
 | 
				
			|||||||
	if (pack === 'astro') {
 | 
						if (pack === 'astro') {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			...metaData,
 | 
								...metaData,
 | 
				
			||||||
			installCommand: `yarn install`,
 | 
								...defaultBuildAndDeploy(packageManager),
 | 
				
			||||||
			buildCommand: `yarn build`,
 | 
					 | 
				
			||||||
			startCommand: null,
 | 
					 | 
				
			||||||
			publishDirectory: `dist`,
 | 
								publishDirectory: `dist`,
 | 
				
			||||||
			port: 80
 | 
								port: 80
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -138,9 +136,7 @@ export function findBuildPack(pack, packageManager = 'npm') {
 | 
				
			|||||||
	if (pack === 'eleventy') {
 | 
						if (pack === 'eleventy') {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			...metaData,
 | 
								...metaData,
 | 
				
			||||||
			installCommand: `yarn install`,
 | 
								...defaultBuildAndDeploy(packageManager),
 | 
				
			||||||
			buildCommand: `yarn build`,
 | 
					 | 
				
			||||||
			startCommand: null,
 | 
					 | 
				
			||||||
			publishDirectory: `_site`,
 | 
								publishDirectory: `_site`,
 | 
				
			||||||
			port: 80
 | 
								port: 80
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,7 +138,18 @@ export async function getApplicationWebhook({
 | 
				
			|||||||
				return s;
 | 
									return s;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return { ...application };
 | 
							const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
 | 
				
			||||||
 | 
								application.buildPack
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Set default build images
 | 
				
			||||||
 | 
							if (!application.baseImage) {
 | 
				
			||||||
 | 
								application.baseImage = baseImage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!application.baseBuildImage) {
 | 
				
			||||||
 | 
								application.baseBuildImage = baseBuildImage;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return { ...application, baseBuildImages, baseImages };
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		throw { status: 404, body: { message: e.message } };
 | 
							throw { status: 404, body: { message: e.message } };
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -267,6 +278,7 @@ export async function configureApplication({
 | 
				
			|||||||
	name,
 | 
						name,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
	port,
 | 
						port,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
	installCommand,
 | 
						installCommand,
 | 
				
			||||||
	buildCommand,
 | 
						buildCommand,
 | 
				
			||||||
	startCommand,
 | 
						startCommand,
 | 
				
			||||||
@@ -286,6 +298,7 @@ export async function configureApplication({
 | 
				
			|||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
	port: number;
 | 
						port: number;
 | 
				
			||||||
 | 
						exposePort: number;
 | 
				
			||||||
	installCommand: string;
 | 
						installCommand: string;
 | 
				
			||||||
	buildCommand: string;
 | 
						buildCommand: string;
 | 
				
			||||||
	startCommand: string;
 | 
						startCommand: string;
 | 
				
			||||||
@@ -307,6 +320,7 @@ export async function configureApplication({
 | 
				
			|||||||
			buildPack,
 | 
								buildPack,
 | 
				
			||||||
			fqdn,
 | 
								fqdn,
 | 
				
			||||||
			port,
 | 
								port,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
			installCommand,
 | 
								installCommand,
 | 
				
			||||||
			buildCommand,
 | 
								buildCommand,
 | 
				
			||||||
			startCommand,
 | 
								startCommand,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,10 +51,12 @@ export async function isSecretExists({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export async function isDomainConfigured({
 | 
					export async function isDomainConfigured({
 | 
				
			||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn
 | 
						fqdn,
 | 
				
			||||||
 | 
						checkOwn = false
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
 | 
						checkOwn?: boolean;
 | 
				
			||||||
}): Promise<boolean> {
 | 
					}): Promise<boolean> {
 | 
				
			||||||
	const domain = getDomain(fqdn);
 | 
						const domain = getDomain(fqdn);
 | 
				
			||||||
	const nakedDomain = domain.replace('www.', '');
 | 
						const nakedDomain = domain.replace('www.', '');
 | 
				
			||||||
@@ -72,12 +74,15 @@ export async function isDomainConfigured({
 | 
				
			|||||||
		where: {
 | 
							where: {
 | 
				
			||||||
			OR: [
 | 
								OR: [
 | 
				
			||||||
				{ fqdn: { endsWith: `//${nakedDomain}` } },
 | 
									{ fqdn: { endsWith: `//${nakedDomain}` } },
 | 
				
			||||||
				{ fqdn: { endsWith: `//www.${nakedDomain}` } }
 | 
									{ fqdn: { endsWith: `//www.${nakedDomain}` } },
 | 
				
			||||||
 | 
									{ minio: { apiFqdn: { endsWith: `//${nakedDomain}` } } },
 | 
				
			||||||
 | 
									{ minio: { apiFqdn: { endsWith: `//www.${nakedDomain}` } } }
 | 
				
			||||||
			],
 | 
								],
 | 
				
			||||||
			id: { not: id }
 | 
								id: { not: checkOwn ? undefined : id }
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		select: { fqdn: true }
 | 
							select: { fqdn: true }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const coolifyFqdn = await prisma.setting.findFirst({
 | 
						const coolifyFqdn = await prisma.setting.findFirst({
 | 
				
			||||||
		where: {
 | 
							where: {
 | 
				
			||||||
			OR: [
 | 
								OR: [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ if (!dev) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const prisma = new PrismaClient({
 | 
					export const prisma = new PrismaClient({
 | 
				
			||||||
	errorFormat: 'pretty',
 | 
						errorFormat: 'minimal',
 | 
				
			||||||
	rejectOnNotFound: false
 | 
						rejectOnNotFound: false
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,7 +58,7 @@ export function ErrorHandler(e: {
 | 
				
			|||||||
		truncatedError.message = 'git clone failed';
 | 
							truncatedError.message = 'git clone failed';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!e.message?.includes('Coolify Proxy is not running')) {
 | 
						if (!e.message?.includes('Coolify Proxy is not running')) {
 | 
				
			||||||
		sentry.captureException(truncatedError);
 | 
							// sentry.captureException(truncatedError);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const payload = {
 | 
						const payload = {
 | 
				
			||||||
		status: truncatedError.status || 500,
 | 
							status: truncatedError.status || 500,
 | 
				
			||||||
@@ -149,6 +149,19 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
 | 
				
			|||||||
				MONGODB_ROOT_PASSWORD: string;
 | 
									MONGODB_ROOT_PASSWORD: string;
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
	  }
 | 
						  }
 | 
				
			||||||
 | 
						| {
 | 
				
			||||||
 | 
								volume: string;
 | 
				
			||||||
 | 
								image: string;
 | 
				
			||||||
 | 
								ulimits: Record<string, unknown>;
 | 
				
			||||||
 | 
								privatePort: number;
 | 
				
			||||||
 | 
								environmentVariables: {
 | 
				
			||||||
 | 
									MARIADB_ROOT_USER: string;
 | 
				
			||||||
 | 
									MARIADB_ROOT_PASSWORD: string;
 | 
				
			||||||
 | 
									MARIADB_USER: string;
 | 
				
			||||||
 | 
									MARIADB_PASSWORD: string;
 | 
				
			||||||
 | 
									MARIADB_DATABASE: string;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
	| {
 | 
						| {
 | 
				
			||||||
			volume: string;
 | 
								volume: string;
 | 
				
			||||||
			image: string;
 | 
								image: string;
 | 
				
			||||||
@@ -207,6 +220,20 @@ export function generateDatabaseConfiguration(database: Database & { settings: D
 | 
				
			|||||||
			volume: `${id}-${type}-data:/bitnami/mysql/data`,
 | 
								volume: `${id}-${type}-data:/bitnami/mysql/data`,
 | 
				
			||||||
			ulimits: {}
 | 
								ulimits: {}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
						} else if (type === 'mariadb') {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								privatePort: 3306,
 | 
				
			||||||
 | 
								environmentVariables: {
 | 
				
			||||||
 | 
									MARIADB_ROOT_USER: rootUser,
 | 
				
			||||||
 | 
									MARIADB_ROOT_PASSWORD: rootUserPassword,
 | 
				
			||||||
 | 
									MARIADB_USER: dbUser,
 | 
				
			||||||
 | 
									MARIADB_PASSWORD: dbUserPassword,
 | 
				
			||||||
 | 
									MARIADB_DATABASE: defaultDatabase
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								image: `${baseImage}:${version}`,
 | 
				
			||||||
 | 
								volume: `${id}-${type}-data:/bitnami/mariadb`,
 | 
				
			||||||
 | 
								ulimits: {}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
	} else if (type === 'mongodb') {
 | 
						} else if (type === 'mongodb') {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			privatePort: 27017,
 | 
								privatePort: 27017,
 | 
				
			||||||
@@ -278,6 +305,12 @@ export async function getFreePort() {
 | 
				
			|||||||
			select: { mysqlPublicPort: true }
 | 
								select: { mysqlPublicPort: true }
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	).map((a) => a.mysqlPublicPort);
 | 
						).map((a) => a.mysqlPublicPort);
 | 
				
			||||||
	const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed];
 | 
						const minioUsed = await (
 | 
				
			||||||
 | 
							await prisma.minio.findMany({
 | 
				
			||||||
 | 
								where: { publicPort: { not: null } },
 | 
				
			||||||
 | 
								select: { publicPort: true }
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						).map((a) => a.publicPort);
 | 
				
			||||||
 | 
						const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
 | 
				
			||||||
	return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts });
 | 
						return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -184,6 +184,10 @@ export async function updatePasswordInDb(database, user, newPassword, isRoot) {
 | 
				
			|||||||
			await asyncExecShell(
 | 
								await asyncExecShell(
 | 
				
			||||||
				`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"ALTER USER '${user}'@'%' IDENTIFIED WITH caching_sha2_password BY '${newPassword}';\"`
 | 
									`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"ALTER USER '${user}'@'%' IDENTIFIED WITH caching_sha2_password BY '${newPassword}';\"`
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
 | 
							} else if (type === 'mariadb') {
 | 
				
			||||||
 | 
								await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST=${host} docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"SET PASSWORD FOR '${user}'@'%' = PASSWORD('${newPassword}');\"`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
		} else if (type === 'postgresql') {
 | 
							} else if (type === 'postgresql') {
 | 
				
			||||||
			if (isRoot) {
 | 
								if (isRoot) {
 | 
				
			||||||
				await asyncExecShell(
 | 
									await asyncExecShell(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { asyncExecShell, getEngine } from '$lib/common';
 | 
					import { asyncExecShell, getEngine } from '$lib/common';
 | 
				
			||||||
import { dockerInstance } from '$lib/docker';
 | 
					import { dockerInstance } from '$lib/docker';
 | 
				
			||||||
import { startCoolifyProxy } from '$lib/haproxy';
 | 
					import { startCoolifyProxy, startTraefikProxy } from '$lib/haproxy';
 | 
				
			||||||
import { getDatabaseImage } from '.';
 | 
					import { getDatabaseImage } from '.';
 | 
				
			||||||
import { prisma } from './common';
 | 
					import { prisma } from './common';
 | 
				
			||||||
import type { DestinationDocker, Service, Application, Prisma } from '@prisma/client';
 | 
					import type { DestinationDocker, Service, Application, Prisma } from '@prisma/client';
 | 
				
			||||||
@@ -125,7 +125,14 @@ export async function newLocalDestination({
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
 | 
							await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (isCoolifyProxyUsed) await startCoolifyProxy(engine);
 | 
						if (isCoolifyProxyUsed) {
 | 
				
			||||||
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
 | 
							if (settings?.isTraefikUsed) {
 | 
				
			||||||
 | 
								await startTraefikProxy(engine);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await startCoolifyProxy(engine);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return destination.id;
 | 
						return destination.id;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export async function removeDestination({ id }: Pick<DestinationDocker, 'id'>): Promise<void> {
 | 
					export async function removeDestination({ id }: Pick<DestinationDocker, 'id'>): Promise<void> {
 | 
				
			||||||
@@ -133,12 +140,14 @@ export async function removeDestination({ id }: Pick<DestinationDocker, 'id'>):
 | 
				
			|||||||
	if (destination.isCoolifyProxyUsed) {
 | 
						if (destination.isCoolifyProxyUsed) {
 | 
				
			||||||
		const host = getEngine(destination.engine);
 | 
							const host = getEngine(destination.engine);
 | 
				
			||||||
		const { network } = destination;
 | 
							const { network } = destination;
 | 
				
			||||||
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
 | 
							const containerName = settings.isTraefikUsed ? 'coolify-proxy' : 'coolify-haproxy';
 | 
				
			||||||
		const { stdout: found } = await asyncExecShell(
 | 
							const { stdout: found } = await asyncExecShell(
 | 
				
			||||||
			`DOCKER_HOST=${host} docker ps -a --filter network=${network} --filter name=coolify-haproxy --format '{{.}}'`
 | 
								`DOCKER_HOST=${host} docker ps -a --filter network=${network} --filter name=${containerName} --format '{{.}}'`
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		if (found) {
 | 
							if (found) {
 | 
				
			||||||
			await asyncExecShell(
 | 
								await asyncExecShell(
 | 
				
			||||||
				`DOCKER_HOST="${host}" docker network disconnect ${network} coolify-haproxy`
 | 
									`DOCKER_HOST="${host}" docker network disconnect ${network} ${containerName}`
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			await asyncExecShell(`DOCKER_HOST="${host}" docker network rm ${network}`);
 | 
								await asyncExecShell(`DOCKER_HOST="${host}" docker network rm ${network}`);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -327,35 +327,62 @@ export async function updatePlausibleAnalyticsService({
 | 
				
			|||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
	email,
 | 
						email,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
	username,
 | 
						username,
 | 
				
			||||||
	name
 | 
						name,
 | 
				
			||||||
 | 
						scriptName
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
	email: string;
 | 
						email: string;
 | 
				
			||||||
	username: string;
 | 
						username: string;
 | 
				
			||||||
 | 
						scriptName: string;
 | 
				
			||||||
}): Promise<void> {
 | 
					}): Promise<void> {
 | 
				
			||||||
	await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
 | 
						await prisma.plausibleAnalytics.update({
 | 
				
			||||||
	await prisma.service.update({ where: { id }, data: { name, fqdn } });
 | 
							where: { serviceId: id },
 | 
				
			||||||
 | 
							data: { email, username, scriptName }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateService({
 | 
					export async function updateService({
 | 
				
			||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
	name
 | 
						name
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
}): Promise<Service> {
 | 
					}): Promise<Service> {
 | 
				
			||||||
	return await prisma.service.update({ where: { id }, data: { fqdn, name } });
 | 
						return await prisma.service.update({ where: { id }, data: { fqdn, name, exposePort } });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export async function updateMinioService({
 | 
				
			||||||
 | 
						id,
 | 
				
			||||||
 | 
						fqdn,
 | 
				
			||||||
 | 
						apiFqdn,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
 | 
						name
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
						id: string;
 | 
				
			||||||
 | 
						fqdn: string;
 | 
				
			||||||
 | 
						apiFqdn: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
					}): Promise<Service> {
 | 
				
			||||||
 | 
						return await prisma.service.update({
 | 
				
			||||||
 | 
							where: { id },
 | 
				
			||||||
 | 
							data: { fqdn, name, exposePort, minio: { update: { apiFqdn } } }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function updateFiderService({
 | 
					export async function updateFiderService({
 | 
				
			||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
	emailNoreply,
 | 
						emailNoreply,
 | 
				
			||||||
	emailMailgunApiKey,
 | 
						emailMailgunApiKey,
 | 
				
			||||||
	emailMailgunDomain,
 | 
						emailMailgunDomain,
 | 
				
			||||||
@@ -368,6 +395,7 @@ export async function updateFiderService({
 | 
				
			|||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
	emailNoreply: string;
 | 
						emailNoreply: string;
 | 
				
			||||||
	emailMailgunApiKey: string;
 | 
						emailMailgunApiKey: string;
 | 
				
			||||||
@@ -384,6 +412,7 @@ export async function updateFiderService({
 | 
				
			|||||||
		data: {
 | 
							data: {
 | 
				
			||||||
			fqdn,
 | 
								fqdn,
 | 
				
			||||||
			name,
 | 
								name,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
			fider: {
 | 
								fider: {
 | 
				
			||||||
				update: {
 | 
									update: {
 | 
				
			||||||
					emailNoreply,
 | 
										emailNoreply,
 | 
				
			||||||
@@ -405,22 +434,49 @@ export async function updateWordpress({
 | 
				
			|||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
 | 
						ownMysql,
 | 
				
			||||||
	mysqlDatabase,
 | 
						mysqlDatabase,
 | 
				
			||||||
	extraConfig
 | 
						extraConfig,
 | 
				
			||||||
 | 
						mysqlHost,
 | 
				
			||||||
 | 
						mysqlPort,
 | 
				
			||||||
 | 
						mysqlUser,
 | 
				
			||||||
 | 
						mysqlPassword
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
 | 
						ownMysql: boolean;
 | 
				
			||||||
	mysqlDatabase: string;
 | 
						mysqlDatabase: string;
 | 
				
			||||||
	extraConfig: string;
 | 
						extraConfig: string;
 | 
				
			||||||
 | 
						mysqlHost?: string;
 | 
				
			||||||
 | 
						mysqlPort?: number;
 | 
				
			||||||
 | 
						mysqlUser?: string;
 | 
				
			||||||
 | 
						mysqlPassword?: string;
 | 
				
			||||||
}): Promise<Service> {
 | 
					}): Promise<Service> {
 | 
				
			||||||
 | 
						mysqlPassword = encrypt(mysqlPassword);
 | 
				
			||||||
	return await prisma.service.update({
 | 
						return await prisma.service.update({
 | 
				
			||||||
		where: { id },
 | 
							where: { id },
 | 
				
			||||||
		data: { fqdn, name, wordpress: { update: { mysqlDatabase, extraConfig } } }
 | 
							data: {
 | 
				
			||||||
 | 
								fqdn,
 | 
				
			||||||
 | 
								name,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
 | 
								wordpress: {
 | 
				
			||||||
 | 
									update: {
 | 
				
			||||||
 | 
										mysqlDatabase,
 | 
				
			||||||
 | 
										extraConfig,
 | 
				
			||||||
 | 
										mysqlHost,
 | 
				
			||||||
 | 
										mysqlUser,
 | 
				
			||||||
 | 
										mysqlPassword,
 | 
				
			||||||
 | 
										mysqlPort
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateMinioService({
 | 
					export async function updateMinioServicePort({
 | 
				
			||||||
	id,
 | 
						id,
 | 
				
			||||||
	publicPort
 | 
						publicPort
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
@@ -434,16 +490,18 @@ export async function updateGhostService({
 | 
				
			|||||||
	id,
 | 
						id,
 | 
				
			||||||
	fqdn,
 | 
						fqdn,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
 | 
						exposePort,
 | 
				
			||||||
	mariadbDatabase
 | 
						mariadbDatabase
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
	id: string;
 | 
						id: string;
 | 
				
			||||||
	fqdn: string;
 | 
						fqdn: string;
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
	mariadbDatabase: string;
 | 
						mariadbDatabase: string;
 | 
				
			||||||
}): Promise<Service> {
 | 
					}): Promise<Service> {
 | 
				
			||||||
	return await prisma.service.update({
 | 
						return await prisma.service.update({
 | 
				
			||||||
		where: { id },
 | 
							where: { id },
 | 
				
			||||||
		data: { fqdn, name, ghost: { update: { mariadbDatabase } } }
 | 
							data: { fqdn, name, exposePort, ghost: { update: { mariadbDatabase } } }
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,9 @@ frontend http
 | 
				
			|||||||
  http-request redirect location {{{redirectValue}}} code ${
 | 
					  http-request redirect location {{{redirectValue}}} code ${
 | 
				
			||||||
		dev ? 302 : 301
 | 
							dev ? 302 : 301
 | 
				
			||||||
	} if { req.hdr(host) -i {{redirectTo}} }
 | 
						} if { req.hdr(host) -i {{redirectTo}} }
 | 
				
			||||||
 | 
					  {{#scriptName}}
 | 
				
			||||||
 | 
					    http-request set-path /js/plausible.js if { hdr(host) -i {{domain}} } { path_beg -i /js/{{scriptName}} }
 | 
				
			||||||
 | 
					  {{/scriptName}}
 | 
				
			||||||
  {{/services}}
 | 
					  {{/services}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {{#coolify}}
 | 
					  {{#coolify}}
 | 
				
			||||||
@@ -218,7 +221,15 @@ export async function configureHAProxy(): Promise<void> {
 | 
				
			|||||||
	const services = await listServicesWithIncludes();
 | 
						const services = await listServicesWithIncludes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (const service of services) {
 | 
						for (const service of services) {
 | 
				
			||||||
		const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
 | 
							const {
 | 
				
			||||||
 | 
								fqdn,
 | 
				
			||||||
 | 
								id,
 | 
				
			||||||
 | 
								type,
 | 
				
			||||||
 | 
								destinationDocker,
 | 
				
			||||||
 | 
								destinationDockerId,
 | 
				
			||||||
 | 
								updatedAt,
 | 
				
			||||||
 | 
								plausibleAnalytics
 | 
				
			||||||
 | 
							} = service;
 | 
				
			||||||
		if (destinationDockerId) {
 | 
							if (destinationDockerId) {
 | 
				
			||||||
			const { engine } = destinationDocker;
 | 
								const { engine } = destinationDocker;
 | 
				
			||||||
			const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
 | 
								const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
 | 
				
			||||||
@@ -232,6 +243,12 @@ export async function configureHAProxy(): Promise<void> {
 | 
				
			|||||||
					const isWWW = fqdn.includes('www.');
 | 
										const isWWW = fqdn.includes('www.');
 | 
				
			||||||
					const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
 | 
										const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
 | 
				
			||||||
					if (isRunning) {
 | 
										if (isRunning) {
 | 
				
			||||||
 | 
											// Plausible Analytics custom script
 | 
				
			||||||
 | 
											let scriptName = false;
 | 
				
			||||||
 | 
											if (type === 'plausibleanalytics' && plausibleAnalytics.scriptName !== 'plausible.js') {
 | 
				
			||||||
 | 
												scriptName = plausibleAnalytics.scriptName;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						data.services.push({
 | 
											data.services.push({
 | 
				
			||||||
							id,
 | 
												id,
 | 
				
			||||||
							port,
 | 
												port,
 | 
				
			||||||
@@ -241,7 +258,8 @@ export async function configureHAProxy(): Promise<void> {
 | 
				
			|||||||
							isHttps,
 | 
												isHttps,
 | 
				
			||||||
							redirectValue,
 | 
												redirectValue,
 | 
				
			||||||
							redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
 | 
												redirectTo: isWWW ? domain.replace('www.', '') : 'www.' + domain,
 | 
				
			||||||
							updatedAt: updatedAt.getTime()
 | 
												updatedAt: updatedAt.getTime(),
 | 
				
			||||||
 | 
												scriptName
 | 
				
			||||||
						});
 | 
											});
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,12 +3,22 @@ import { asyncExecShell, getEngine } from '$lib/common';
 | 
				
			|||||||
import got, { type Got, type Response } from 'got';
 | 
					import got, { type Got, type Response } from 'got';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import type { DestinationDocker } from '@prisma/client';
 | 
					import type { DestinationDocker } from '@prisma/client';
 | 
				
			||||||
 | 
					import fs from 'fs/promises';
 | 
				
			||||||
 | 
					import yaml from 'js-yaml';
 | 
				
			||||||
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
 | 
					const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const defaultProxyImage = `coolify-haproxy-alpine:latest`;
 | 
					export const defaultProxyImage = `coolify-haproxy-alpine:latest`;
 | 
				
			||||||
export const defaultProxyImageTcp = `coolify-haproxy-tcp-alpine:latest`;
 | 
					export const defaultProxyImageTcp = `coolify-haproxy-tcp-alpine:latest`;
 | 
				
			||||||
export const defaultProxyImageHttp = `coolify-haproxy-http-alpine:latest`;
 | 
					export const defaultProxyImageHttp = `coolify-haproxy-http-alpine:latest`;
 | 
				
			||||||
 | 
					export const defaultTraefikImage = `traefik:v2.6`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mainTraefikEndpoint = dev
 | 
				
			||||||
 | 
						? 'http://host.docker.internal:3000/webhooks/traefik/main.json'
 | 
				
			||||||
 | 
						: 'http://coolify:3000/webhooks/traefik/main.json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const otherTraefikEndpoint = dev
 | 
				
			||||||
 | 
						? 'http://host.docker.internal:3000/webhooks/traefik/other.json'
 | 
				
			||||||
 | 
						: 'http://coolify:3000/webhooks/traefik/other.json';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function haproxyInstance(): Promise<Got> {
 | 
					export async function haproxyInstance(): Promise<Got> {
 | 
				
			||||||
	const { proxyPassword } = await db.listSettings();
 | 
						const { proxyPassword } = await db.listSettings();
 | 
				
			||||||
@@ -98,13 +108,21 @@ export async function checkHAProxy(haproxy?: Got): Promise<void> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function stopTcpHttpProxy(
 | 
					export async function stopTcpHttpProxy(
 | 
				
			||||||
 | 
						id: string,
 | 
				
			||||||
	destinationDocker: DestinationDocker,
 | 
						destinationDocker: DestinationDocker,
 | 
				
			||||||
	publicPort: number
 | 
						publicPort: number,
 | 
				
			||||||
 | 
						forceName: string = null
 | 
				
			||||||
): Promise<{ stdout: string; stderr: string } | Error> {
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
	const { engine } = destinationDocker;
 | 
						const { engine } = destinationDocker;
 | 
				
			||||||
	const host = getEngine(engine);
 | 
						const host = getEngine(engine);
 | 
				
			||||||
	const containerName = `haproxy-for-${publicPort}`;
 | 
						const settings = await db.listSettings();
 | 
				
			||||||
 | 
						let containerName = `${id}-${publicPort}`;
 | 
				
			||||||
 | 
						if (!settings.isTraefikUsed) {
 | 
				
			||||||
 | 
							containerName = `haproxy-for-${publicPort}`;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (forceName) containerName = forceName;
 | 
				
			||||||
	const found = await checkContainer(engine, containerName);
 | 
						const found = await checkContainer(engine, containerName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		if (found) {
 | 
							if (found) {
 | 
				
			||||||
			return await asyncExecShell(
 | 
								return await asyncExecShell(
 | 
				
			||||||
@@ -115,12 +133,76 @@ export async function stopTcpHttpProxy(
 | 
				
			|||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export async function startTcpProxy(
 | 
					export async function startTraefikTCPProxy(
 | 
				
			||||||
	destinationDocker: DestinationDocker,
 | 
						destinationDocker: DestinationDocker,
 | 
				
			||||||
	id: string,
 | 
						id: string,
 | 
				
			||||||
	publicPort: number,
 | 
						publicPort: number,
 | 
				
			||||||
	privatePort: number,
 | 
						privatePort: number,
 | 
				
			||||||
	volume?: string
 | 
						type?: string
 | 
				
			||||||
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
 | 
						const { network, engine } = destinationDocker;
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
						const containerName = `${id}-${publicPort}`;
 | 
				
			||||||
 | 
						const found = await checkContainer(engine, containerName, true);
 | 
				
			||||||
 | 
						let dependentId = id;
 | 
				
			||||||
 | 
						if (type === 'wordpressftp') dependentId = `${id}-ftp`;
 | 
				
			||||||
 | 
						const foundDependentContainer = await checkContainer(engine, dependentId, true);
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							if (foundDependentContainer && !found) {
 | 
				
			||||||
 | 
								const { stdout: Config } = await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								const ip = JSON.parse(Config)[0].Gateway;
 | 
				
			||||||
 | 
								const tcpProxy = {
 | 
				
			||||||
 | 
									version: '3.5',
 | 
				
			||||||
 | 
									services: {
 | 
				
			||||||
 | 
										[`${id}-${publicPort}`]: {
 | 
				
			||||||
 | 
											container_name: containerName,
 | 
				
			||||||
 | 
											image: 'traefik:v2.6',
 | 
				
			||||||
 | 
											command: [
 | 
				
			||||||
 | 
												`--entrypoints.tcp.address=:${publicPort}`,
 | 
				
			||||||
 | 
												`--providers.http.endpoint=${otherTraefikEndpoint}?id=${id}&privatePort=${privatePort}&publicPort=${publicPort}&type=tcp`,
 | 
				
			||||||
 | 
												'--providers.http.pollTimeout=2s',
 | 
				
			||||||
 | 
												'--log.level=error'
 | 
				
			||||||
 | 
											],
 | 
				
			||||||
 | 
											ports: [`${publicPort}:${publicPort}`],
 | 
				
			||||||
 | 
											extra_hosts: ['host.docker.internal:host-gateway', `host.docker.internal:${ip}`],
 | 
				
			||||||
 | 
											volumes: ['/var/run/docker.sock:/var/run/docker.sock'],
 | 
				
			||||||
 | 
											networks: ['coolify-infra', network]
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									networks: {
 | 
				
			||||||
 | 
										[network]: {
 | 
				
			||||||
 | 
											external: false,
 | 
				
			||||||
 | 
											name: network
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										'coolify-infra': {
 | 
				
			||||||
 | 
											external: false,
 | 
				
			||||||
 | 
											name: 'coolify-infra'
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								await fs.writeFile(`/tmp/docker-compose-${id}.yaml`, yaml.dump(tcpProxy));
 | 
				
			||||||
 | 
								await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST=${host} docker compose -f /tmp/docker-compose-${id}.yaml up -d`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								await fs.rm(`/tmp/docker-compose-${id}.yaml`);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!foundDependentContainer && found) {
 | 
				
			||||||
 | 
								return await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST=${host} docker stop -t 0 ${containerName} && docker rm ${containerName}`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(error);
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export async function startTcpProxy(
 | 
				
			||||||
 | 
						destinationDocker: DestinationDocker,
 | 
				
			||||||
 | 
						id: string,
 | 
				
			||||||
 | 
						publicPort: number,
 | 
				
			||||||
 | 
						privatePort: number
 | 
				
			||||||
): Promise<{ stdout: string; stderr: string } | Error> {
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
	const { network, engine } = destinationDocker;
 | 
						const { network, engine } = destinationDocker;
 | 
				
			||||||
	const host = getEngine(engine);
 | 
						const host = getEngine(engine);
 | 
				
			||||||
@@ -128,7 +210,6 @@ export async function startTcpProxy(
 | 
				
			|||||||
	const containerName = `haproxy-for-${publicPort}`;
 | 
						const containerName = `haproxy-for-${publicPort}`;
 | 
				
			||||||
	const found = await checkContainer(engine, containerName, true);
 | 
						const found = await checkContainer(engine, containerName, true);
 | 
				
			||||||
	const foundDependentContainer = await checkContainer(engine, id, true);
 | 
						const foundDependentContainer = await checkContainer(engine, id, true);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		if (foundDependentContainer && !found) {
 | 
							if (foundDependentContainer && !found) {
 | 
				
			||||||
			const { stdout: Config } = await asyncExecShell(
 | 
								const { stdout: Config } = await asyncExecShell(
 | 
				
			||||||
@@ -136,9 +217,7 @@ export async function startTcpProxy(
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			const ip = JSON.parse(Config)[0].Gateway;
 | 
								const ip = JSON.parse(Config)[0].Gateway;
 | 
				
			||||||
			return await asyncExecShell(
 | 
								return await asyncExecShell(
 | 
				
			||||||
				`DOCKER_HOST=${host} docker run --restart always -e PORT=${publicPort} -e APP=${id} -e PRIVATE_PORT=${privatePort} --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} ${
 | 
									`DOCKER_HOST=${host} docker run --restart always -e PORT=${publicPort} -e APP=${id} -e PRIVATE_PORT=${privatePort} --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} -d coollabsio/${defaultProxyImageTcp}`
 | 
				
			||||||
					volume ? `-v ${volume}` : ''
 | 
					 | 
				
			||||||
				} -d coollabsio/${defaultProxyImageTcp}`
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!foundDependentContainer && found) {
 | 
							if (!foundDependentContainer && found) {
 | 
				
			||||||
@@ -151,6 +230,75 @@ export async function startTcpProxy(
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function startTraefikHTTPProxy(
 | 
				
			||||||
 | 
						destinationDocker: DestinationDocker,
 | 
				
			||||||
 | 
						id: string,
 | 
				
			||||||
 | 
						publicPort: number,
 | 
				
			||||||
 | 
						privatePort: number
 | 
				
			||||||
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
 | 
						const { network, engine } = destinationDocker;
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const containerName = `${id}-${publicPort}`;
 | 
				
			||||||
 | 
						const found = await checkContainer(engine, containerName, true);
 | 
				
			||||||
 | 
						const foundDependentContainer = await checkContainer(engine, id, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							if (foundDependentContainer && !found) {
 | 
				
			||||||
 | 
								const { stdout: Config } = await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								const ip = JSON.parse(Config)[0].Gateway;
 | 
				
			||||||
 | 
								const tcpProxy = {
 | 
				
			||||||
 | 
									version: '3.5',
 | 
				
			||||||
 | 
									services: {
 | 
				
			||||||
 | 
										[`${id}-${publicPort}`]: {
 | 
				
			||||||
 | 
											container_name: containerName,
 | 
				
			||||||
 | 
											image: 'traefik:v2.6',
 | 
				
			||||||
 | 
											command: [
 | 
				
			||||||
 | 
												`--entrypoints.http.address=:${publicPort}`,
 | 
				
			||||||
 | 
												`--providers.http.endpoint=${otherTraefikEndpoint}?id=${id}&privatePort=${privatePort}&publicPort=${publicPort}&type=http`,
 | 
				
			||||||
 | 
												'--providers.http.pollTimeout=2s',
 | 
				
			||||||
 | 
												'--certificatesresolvers.letsencrypt.acme.httpchallenge=true',
 | 
				
			||||||
 | 
												'--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json',
 | 
				
			||||||
 | 
												'--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http',
 | 
				
			||||||
 | 
												'--log.level=error'
 | 
				
			||||||
 | 
											],
 | 
				
			||||||
 | 
											ports: [`${publicPort}:${publicPort}`],
 | 
				
			||||||
 | 
											extra_hosts: ['host.docker.internal:host-gateway', `host.docker.internal:${ip}`],
 | 
				
			||||||
 | 
											networks: ['coolify-infra', network],
 | 
				
			||||||
 | 
											volumes: ['coolify-traefik-letsencrypt:/etc/traefik/acme']
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									networks: {
 | 
				
			||||||
 | 
										[network]: {
 | 
				
			||||||
 | 
											external: false,
 | 
				
			||||||
 | 
											name: network
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										'coolify-infra': {
 | 
				
			||||||
 | 
											external: false,
 | 
				
			||||||
 | 
											name: 'coolify-infra'
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									volumes: {
 | 
				
			||||||
 | 
										'coolify-traefik-letsencrypt': {}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								await fs.writeFile(`/tmp/docker-compose-${id}.yaml`, yaml.dump(tcpProxy));
 | 
				
			||||||
 | 
								await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST=${host} docker compose -f /tmp/docker-compose-${id}.yaml up -d`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								await fs.rm(`/tmp/docker-compose-${id}.yaml`);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!foundDependentContainer && found) {
 | 
				
			||||||
 | 
								return await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST=${host} docker stop -t 0 ${containerName} && docker rm ${containerName}`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export async function startHttpProxy(
 | 
					export async function startHttpProxy(
 | 
				
			||||||
	destinationDocker: DestinationDocker,
 | 
						destinationDocker: DestinationDocker,
 | 
				
			||||||
	id: string,
 | 
						id: string,
 | 
				
			||||||
@@ -197,10 +345,50 @@ export async function startCoolifyProxy(engine: string): Promise<void> {
 | 
				
			|||||||
			`DOCKER_HOST="${host}" docker run -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restart always --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' -v coolify-ssl-certs:/usr/local/etc/haproxy/ssl --network coolify-infra -p "80:80" -p "443:443" -p "8404:8404" -p "5555:5555" -p "5000:5000" --name coolify-haproxy -d coollabsio/${defaultProxyImage}`
 | 
								`DOCKER_HOST="${host}" docker run -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restart always --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' -v coolify-ssl-certs:/usr/local/etc/haproxy/ssl --network coolify-infra -p "80:80" -p "443:443" -p "8404:8404" -p "5555:5555" -p "5000:5000" --name coolify-haproxy -d coollabsio/${defaultProxyImage}`
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
 | 
							await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
 | 
				
			||||||
 | 
							await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	await configureNetworkCoolifyProxy(engine);
 | 
						await configureNetworkCoolifyProxy(engine);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function startTraefikProxy(engine: string): Promise<void> {
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
						const found = await checkContainer(engine, 'coolify-proxy', true);
 | 
				
			||||||
 | 
						const { id, proxyPassword, proxyUser } = await db.listSettings();
 | 
				
			||||||
 | 
						if (!found) {
 | 
				
			||||||
 | 
							const { stdout: Config } = await asyncExecShell(
 | 
				
			||||||
 | 
								`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							const ip = JSON.parse(Config)[0].Gateway;
 | 
				
			||||||
 | 
							await asyncExecShell(
 | 
				
			||||||
 | 
								`DOCKER_HOST="${host}" docker run --restart always \
 | 
				
			||||||
 | 
								--add-host 'host.docker.internal:host-gateway' \
 | 
				
			||||||
 | 
								--add-host 'host.docker.internal:${ip}' \
 | 
				
			||||||
 | 
								-v coolify-traefik-letsencrypt:/etc/traefik/acme \
 | 
				
			||||||
 | 
								-v /var/run/docker.sock:/var/run/docker.sock \
 | 
				
			||||||
 | 
								--network coolify-infra \
 | 
				
			||||||
 | 
								-p "80:80" \
 | 
				
			||||||
 | 
								-p "443:443" \
 | 
				
			||||||
 | 
								-p "8080:8080" \
 | 
				
			||||||
 | 
								--name coolify-proxy \
 | 
				
			||||||
 | 
								-d ${defaultTraefikImage} \
 | 
				
			||||||
 | 
								--api.insecure=true \
 | 
				
			||||||
 | 
								--entrypoints.web.address=:80 \
 | 
				
			||||||
 | 
								--entrypoints.websecure.address=:443 \
 | 
				
			||||||
 | 
								--providers.docker=true \
 | 
				
			||||||
 | 
								--providers.docker.exposedbydefault=false \
 | 
				
			||||||
 | 
								--providers.http.endpoint=${mainTraefikEndpoint} \
 | 
				
			||||||
 | 
								--providers.http.pollTimeout=5s \
 | 
				
			||||||
 | 
								--certificatesresolvers.letsencrypt.acme.httpchallenge=true \
 | 
				
			||||||
 | 
								--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json \
 | 
				
			||||||
 | 
								--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web \
 | 
				
			||||||
 | 
								--log.level=error`
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
 | 
				
			||||||
 | 
							await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						await configureNetworkTraefikProxy(engine);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function isContainerExited(engine: string, containerName: string): Promise<boolean> {
 | 
					export async function isContainerExited(engine: string, containerName: string): Promise<boolean> {
 | 
				
			||||||
	let isExited = false;
 | 
						let isExited = false;
 | 
				
			||||||
	const host = getEngine(engine);
 | 
						const host = getEngine(engine);
 | 
				
			||||||
@@ -245,6 +433,21 @@ export async function checkContainer(
 | 
				
			|||||||
	return containerFound;
 | 
						return containerFound;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getContainerUsage(engine: string, container: string): Promise<any> {
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const { stdout } = await asyncExecShell(
 | 
				
			||||||
 | 
								`DOCKER_HOST="${host}" docker container stats ${container} --no-stream --no-trunc --format "{{json .}}"`
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							return JSON.parse(stdout);
 | 
				
			||||||
 | 
						} catch (err) {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								MemUsage: 0,
 | 
				
			||||||
 | 
								CPUPerc: 0,
 | 
				
			||||||
 | 
								NetIO: 0
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export async function stopCoolifyProxy(
 | 
					export async function stopCoolifyProxy(
 | 
				
			||||||
	engine: string
 | 
						engine: string
 | 
				
			||||||
): Promise<{ stdout: string; stderr: string } | Error> {
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
@@ -263,6 +466,24 @@ export async function stopCoolifyProxy(
 | 
				
			|||||||
		return error;
 | 
							return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export async function stopTraefikProxy(
 | 
				
			||||||
 | 
						engine: string
 | 
				
			||||||
 | 
					): Promise<{ stdout: string; stderr: string } | Error> {
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
						const found = await checkContainer(engine, 'coolify-proxy');
 | 
				
			||||||
 | 
						await db.setDestinationSettings({ engine, isCoolifyProxyUsed: false });
 | 
				
			||||||
 | 
						const { id } = await db.prisma.setting.findFirst({});
 | 
				
			||||||
 | 
						await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							if (found) {
 | 
				
			||||||
 | 
								await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST="${host}" docker stop -t 0 coolify-proxy && docker rm coolify-proxy`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function configureNetworkCoolifyProxy(engine: string): Promise<void> {
 | 
					export async function configureNetworkCoolifyProxy(engine: string): Promise<void> {
 | 
				
			||||||
	const host = getEngine(engine);
 | 
						const host = getEngine(engine);
 | 
				
			||||||
@@ -279,3 +500,19 @@ export async function configureNetworkCoolifyProxy(engine: string): Promise<void
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function configureNetworkTraefikProxy(engine: string): Promise<void> {
 | 
				
			||||||
 | 
						const host = getEngine(engine);
 | 
				
			||||||
 | 
						const destinations = await db.prisma.destinationDocker.findMany({ where: { engine } });
 | 
				
			||||||
 | 
						const { stdout: networks } = await asyncExecShell(
 | 
				
			||||||
 | 
							`DOCKER_HOST="${host}" docker ps -a --filter name=coolify-proxy --format '{{json .Networks}}'`
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						const configuredNetworks = networks.replace(/"/g, '').replace('\n', '').split(',');
 | 
				
			||||||
 | 
						for (const destination of destinations) {
 | 
				
			||||||
 | 
							if (!configuredNetworks.includes(destination.network)) {
 | 
				
			||||||
 | 
								await asyncExecShell(
 | 
				
			||||||
 | 
									`DOCKER_HOST="${host}" docker network connect ${destination.network} coolify-proxy`
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -290,3 +290,30 @@ export async function generateSSLCerts(): Promise<void> {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function renewSSLCerts(): Promise<void> {
 | 
				
			||||||
 | 
						if (!dev) {
 | 
				
			||||||
 | 
							const host = 'unix:///var/run/docker.sock';
 | 
				
			||||||
 | 
							await asyncExecShell(`docker pull alpine:latest`);
 | 
				
			||||||
 | 
							const certbotImage =
 | 
				
			||||||
 | 
								process.arch === 'x64' ? 'certbot/certbot' : 'certbot/certbot:arm64v8-latest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const { stdout: certificates } = await asyncExecShell(
 | 
				
			||||||
 | 
								`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "ls -1 /etc/letsencrypt/live/ | grep -v README"`
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (const certificate of certificates.trim().split('\n')) {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									await asyncExecShell(
 | 
				
			||||||
 | 
										`DOCKER_HOST=${host} docker run --rm --name certbot-renewal -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" ${certbotImage} --cert-name ${certificate} --logs-dir /etc/letsencrypt/logs renew --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080`
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									await asyncExecShell(
 | 
				
			||||||
 | 
										`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "test -d /etc/letsencrypt/live/${certificate}/ && cat /etc/letsencrypt/live/${certificate}/fullchain.pem /etc/letsencrypt/live/${certificate}/privkey.pem > /app/ssl/${certificate}.pem"`
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
								} catch (error) {
 | 
				
			||||||
 | 
									console.log(error);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							await reloadHaproxy('unix:///var/run/docker.sock');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,13 +178,15 @@
 | 
				
			|||||||
		"delete_application": "Delete application",
 | 
							"delete_application": "Delete application",
 | 
				
			||||||
		"permission_denied_delete_application": "You do not have permission to delete this application",
 | 
							"permission_denied_delete_application": "You do not have permission to delete this application",
 | 
				
			||||||
		"domain_already_in_use": "Domain {{domain}} is already used.",
 | 
							"domain_already_in_use": "Domain {{domain}} is already used.",
 | 
				
			||||||
		"dns_not_set_error": "DNS not set or propogated for {{domain}}.<br><br>Please check your DNS settings.",
 | 
							"dns_not_set_error": "DNS not set correctly or propogated for {{domain}}.<br><br>Please check your DNS settings.",
 | 
				
			||||||
 | 
							"domain_required": "Domain is required.",
 | 
				
			||||||
		"settings_saved": "Settings saved.",
 | 
							"settings_saved": "Settings saved.",
 | 
				
			||||||
		"dns_not_set_partial_error": "DNS not set",
 | 
							"dns_not_set_partial_error": "DNS not set",
 | 
				
			||||||
 | 
							"domain_not_valid": "Could not resolve domain or it's not pointing to the server IP address.<br><br>Please check your DNS configuration and try again.",
 | 
				
			||||||
		"git_source": "Git Source",
 | 
							"git_source": "Git Source",
 | 
				
			||||||
		"git_repository": "Git Repository",
 | 
							"git_repository": "Git Repository",
 | 
				
			||||||
		"build_pack": "Build Pack",
 | 
							"build_pack": "Build Pack",
 | 
				
			||||||
		"base_image": "Deplyoment Image",
 | 
							"base_image": "Deployment Image",
 | 
				
			||||||
		"base_image_explainer": "Image that will be used for the deployment.",
 | 
							"base_image_explainer": "Image that will be used for the deployment.",
 | 
				
			||||||
		"base_build_image": "Build Image",
 | 
							"base_build_image": "Build Image",
 | 
				
			||||||
		"base_build_image_explainer": "Image that will be used during the build process.",
 | 
							"base_build_image_explainer": "Image that will be used during the build process.",
 | 
				
			||||||
@@ -204,6 +206,7 @@
 | 
				
			|||||||
		"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.",
 | 
				
			||||||
		"enable_mr_pr_previews": "Enable MR/PR Previews",
 | 
							"enable_mr_pr_previews": "Enable MR/PR Previews",
 | 
				
			||||||
 | 
							"expose_a_port": "Expose a port",
 | 
				
			||||||
		"enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.",
 | 
							"enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.",
 | 
				
			||||||
		"debug_logs": "Debug Logs",
 | 
							"debug_logs": "Debug Logs",
 | 
				
			||||||
		"enable_debug_log_during_build": "Enable debug logs during build phase.<br><span class='text-red-500 font-bold'>Sensitive information</span> could be visible and saved in logs.",
 | 
							"enable_debug_log_during_build": "Enable debug logs during build phase.<br><span class='text-red-500 font-bold'>Sensitive information</span> could be visible and saved in logs.",
 | 
				
			||||||
@@ -311,7 +314,7 @@
 | 
				
			|||||||
		"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page.",
 | 
							"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page.",
 | 
				
			||||||
		"auto_update_enabled": "Auto update enabled?",
 | 
							"auto_update_enabled": "Auto update enabled?",
 | 
				
			||||||
		"auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.",
 | 
							"auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.",
 | 
				
			||||||
		"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-yellow-500'>both DNS entries</span> set in advance.<br><br>Service needs to be restarted.",
 | 
							"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-yellow-500'>both DNS entries</span> set in advance.",
 | 
				
			||||||
		"is_dns_check_enabled": "DNS check enabled?",
 | 
							"is_dns_check_enabled": "DNS check enabled?",
 | 
				
			||||||
		"is_dns_check_enabled_explainer": "You can disable DNS check before creating SSL certificates.<br><br>Turning it off is useful when Coolify is behind a reverse proxy or tunnel."
 | 
							"is_dns_check_enabled_explainer": "You can disable DNS check before creating SSL certificates.<br><br>Turning it off is useful when Coolify is behind a reverse proxy or tunnel."
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,6 +61,7 @@
 | 
				
			|||||||
		"enable_debug_log_during_build": "Activez les journaux de débogage pendant la phase de build.<br><span class='text-red-500 font-bold'>Les informations sensibles</span> peuvent être visibles et enregistrées dans les journaux.",
 | 
							"enable_debug_log_during_build": "Activez les journaux de débogage pendant la phase de build.<br><span class='text-red-500 font-bold'>Les informations sensibles</span> peuvent être visibles et enregistrées dans les journaux.",
 | 
				
			||||||
		"enable_mr_pr_previews": "Activer les aperçus MR/PR",
 | 
							"enable_mr_pr_previews": "Activer les aperçus MR/PR",
 | 
				
			||||||
		"enable_preview_deploy_mr_pr_requests": "Activez les déploiements de prévisualisation à partir de demandes d'extraction ou de fusion.",
 | 
							"enable_preview_deploy_mr_pr_requests": "Activez les déploiements de prévisualisation à partir de demandes d'extraction ou de fusion.",
 | 
				
			||||||
 | 
							"expose_a_port": "Exposer un port",
 | 
				
			||||||
		"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",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,6 +48,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
		pythonModule,
 | 
							pythonModule,
 | 
				
			||||||
		pythonVariable,
 | 
							pythonVariable,
 | 
				
			||||||
		denoOptions,
 | 
							denoOptions,
 | 
				
			||||||
 | 
							exposePort,
 | 
				
			||||||
		baseImage,
 | 
							baseImage,
 | 
				
			||||||
		baseBuildImage
 | 
							baseBuildImage
 | 
				
			||||||
	} = job.data;
 | 
						} = job.data;
 | 
				
			||||||
@@ -152,6 +153,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
					JSON.stringify({
 | 
										JSON.stringify({
 | 
				
			||||||
						buildPack,
 | 
											buildPack,
 | 
				
			||||||
						port,
 | 
											port,
 | 
				
			||||||
 | 
											exposePort,
 | 
				
			||||||
						installCommand,
 | 
											installCommand,
 | 
				
			||||||
						buildCommand,
 | 
											buildCommand,
 | 
				
			||||||
						startCommand,
 | 
											startCommand,
 | 
				
			||||||
@@ -207,7 +209,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
					tag,
 | 
										tag,
 | 
				
			||||||
					workdir,
 | 
										workdir,
 | 
				
			||||||
					docker,
 | 
										docker,
 | 
				
			||||||
					port,
 | 
										port: exposePort ? `${exposePort}:${port}` : port,
 | 
				
			||||||
					installCommand,
 | 
										installCommand,
 | 
				
			||||||
					buildCommand,
 | 
										buildCommand,
 | 
				
			||||||
					startCommand,
 | 
										startCommand,
 | 
				
			||||||
@@ -263,7 +265,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
			repository,
 | 
								repository,
 | 
				
			||||||
			branch,
 | 
								branch,
 | 
				
			||||||
			projectId,
 | 
								projectId,
 | 
				
			||||||
			port,
 | 
								port: exposePort ? `${exposePort}:${port}` : port,
 | 
				
			||||||
			commit,
 | 
								commit,
 | 
				
			||||||
			installCommand,
 | 
								installCommand,
 | 
				
			||||||
			buildCommand,
 | 
								buildCommand,
 | 
				
			||||||
@@ -298,6 +300,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
						labels,
 | 
											labels,
 | 
				
			||||||
						depends_on: [],
 | 
											depends_on: [],
 | 
				
			||||||
						restart: 'always',
 | 
											restart: 'always',
 | 
				
			||||||
 | 
											...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
 | 
				
			||||||
						// logging: {
 | 
											// logging: {
 | 
				
			||||||
						// 	driver: 'fluentd',
 | 
											// 	driver: 'fluentd',
 | 
				
			||||||
						// },
 | 
											// },
 | 
				
			||||||
@@ -325,7 +328,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
 | 
				
			|||||||
			await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
 | 
								await saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
 | 
				
			||||||
		} catch (error) {
 | 
							} catch (error) {
 | 
				
			||||||
			await saveBuildLog({ line: error, buildId, applicationId });
 | 
								await saveBuildLog({ line: error, buildId, applicationId });
 | 
				
			||||||
			sentry.captureException(error);
 | 
								// sentry.captureException(error);
 | 
				
			||||||
			throw new Error(error);
 | 
								throw new Error(error);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
 | 
							await saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,6 @@ export default async function (): Promise<void> {
 | 
				
			|||||||
		} catch (error) {
 | 
							} catch (error) {
 | 
				
			||||||
			console.log(error);
 | 
								console.log(error);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		console.log(`Is LowDiskSpace detected? ${lowDiskSpace}`);
 | 
					 | 
				
			||||||
		if (lowDiskSpace) {
 | 
							if (lowDiskSpace) {
 | 
				
			||||||
			// Cleanup old coolify images
 | 
								// Cleanup old coolify images
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,8 +116,8 @@ const cron = async (): Promise<void> => {
 | 
				
			|||||||
	await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
 | 
						await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } });
 | 
				
			||||||
	await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
 | 
						await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } });
 | 
				
			||||||
	if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
 | 
						if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } });
 | 
				
			||||||
	await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
 | 
						if (!dev) await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
 | 
				
			||||||
	await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
 | 
						if (!dev) await queue.autoUpdater.add('autoUpdater', {}, { repeat: { every: 60000 } });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
cron().catch((error) => {
 | 
					cron().catch((error) => {
 | 
				
			||||||
	console.log('cron failed to start');
 | 
						console.log('cron failed to start');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler, prisma } from '$lib/database';
 | 
				
			||||||
import { configureHAProxy } from '$lib/haproxy/configuration';
 | 
					import { configureHAProxy } from '$lib/haproxy/configuration';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function (): Promise<void | {
 | 
					export default async function (): Promise<void | {
 | 
				
			||||||
@@ -6,7 +6,10 @@ export default async function (): Promise<void | {
 | 
				
			|||||||
	body: { message: string; error: string };
 | 
						body: { message: string; error: string };
 | 
				
			||||||
}> {
 | 
					}> {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		return await configureHAProxy();
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
 | 
							if (!settings.isTraefikUsed) {
 | 
				
			||||||
 | 
								return await configureHAProxy();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		return ErrorHandler(error.response?.body || error);
 | 
							return ErrorHandler(error.response?.body || error);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,16 @@
 | 
				
			|||||||
import { ErrorHandler, generateDatabaseConfiguration, prisma } from '$lib/database';
 | 
					import { ErrorHandler, generateDatabaseConfiguration, prisma } from '$lib/database';
 | 
				
			||||||
import { startCoolifyProxy, startHttpProxy, startTcpProxy } from '$lib/haproxy';
 | 
					import {
 | 
				
			||||||
 | 
						checkContainer,
 | 
				
			||||||
 | 
						startCoolifyProxy,
 | 
				
			||||||
 | 
						startHttpProxy,
 | 
				
			||||||
 | 
						startTcpProxy,
 | 
				
			||||||
 | 
						startTraefikHTTPProxy,
 | 
				
			||||||
 | 
						startTraefikProxy,
 | 
				
			||||||
 | 
						startTraefikTCPProxy,
 | 
				
			||||||
 | 
						stopCoolifyProxy,
 | 
				
			||||||
 | 
						stopTcpHttpProxy,
 | 
				
			||||||
 | 
						stopTraefikProxy
 | 
				
			||||||
 | 
					} from '$lib/haproxy';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function (): Promise<void | {
 | 
					export default async function (): Promise<void | {
 | 
				
			||||||
	status: number;
 | 
						status: number;
 | 
				
			||||||
@@ -7,12 +18,23 @@ export default async function (): Promise<void | {
 | 
				
			|||||||
}> {
 | 
					}> {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		// Coolify Proxy
 | 
							// Coolify Proxy
 | 
				
			||||||
 | 
							const engine = '/var/run/docker.sock';
 | 
				
			||||||
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
		const localDocker = await prisma.destinationDocker.findFirst({
 | 
							const localDocker = await prisma.destinationDocker.findFirst({
 | 
				
			||||||
			where: { engine: '/var/run/docker.sock' }
 | 
								where: { engine, network: 'coolify' }
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		if (localDocker && localDocker.isCoolifyProxyUsed) {
 | 
							if (localDocker && localDocker.isCoolifyProxyUsed) {
 | 
				
			||||||
			await startCoolifyProxy('/var/run/docker.sock');
 | 
								if (settings.isTraefikUsed) {
 | 
				
			||||||
 | 
									const found = await checkContainer(engine, 'coolify-haproxy');
 | 
				
			||||||
 | 
									if (found) await stopCoolifyProxy(engine);
 | 
				
			||||||
 | 
									await startTraefikProxy(engine);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									const found = await checkContainer(engine, 'coolify-proxy');
 | 
				
			||||||
 | 
									if (found) await stopTraefikProxy(engine);
 | 
				
			||||||
 | 
									await startCoolifyProxy(engine);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TCP Proxies
 | 
							// TCP Proxies
 | 
				
			||||||
		const databasesWithPublicPort = await prisma.database.findMany({
 | 
							const databasesWithPublicPort = await prisma.database.findMany({
 | 
				
			||||||
			where: { publicPort: { not: null } },
 | 
								where: { publicPort: { not: null } },
 | 
				
			||||||
@@ -21,8 +43,16 @@ export default async function (): Promise<void | {
 | 
				
			|||||||
		for (const database of databasesWithPublicPort) {
 | 
							for (const database of databasesWithPublicPort) {
 | 
				
			||||||
			const { destinationDockerId, destinationDocker, publicPort, id } = database;
 | 
								const { destinationDockerId, destinationDocker, publicPort, id } = database;
 | 
				
			||||||
			if (destinationDockerId) {
 | 
								if (destinationDockerId) {
 | 
				
			||||||
				const { privatePort } = generateDatabaseConfiguration(database);
 | 
									if (destinationDocker.isCoolifyProxyUsed) {
 | 
				
			||||||
				await startTcpProxy(destinationDocker, id, publicPort, privatePort);
 | 
										const { privatePort } = generateDatabaseConfiguration(database);
 | 
				
			||||||
 | 
										if (settings.isTraefikUsed) {
 | 
				
			||||||
 | 
											await stopTcpHttpProxy(id, destinationDocker, publicPort, `haproxy-for-${publicPort}`);
 | 
				
			||||||
 | 
											await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											await stopTcpHttpProxy(id, destinationDocker, publicPort, `${id}-${publicPort}`);
 | 
				
			||||||
 | 
											await startTcpProxy(destinationDocker, id, publicPort, privatePort);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const wordpressWithFtp = await prisma.wordpress.findMany({
 | 
							const wordpressWithFtp = await prisma.wordpress.findMany({
 | 
				
			||||||
@@ -33,20 +63,38 @@ export default async function (): Promise<void | {
 | 
				
			|||||||
			const { service, ftpPublicPort } = ftp;
 | 
								const { service, ftpPublicPort } = ftp;
 | 
				
			||||||
			const { destinationDockerId, destinationDocker, id } = service;
 | 
								const { destinationDockerId, destinationDocker, id } = service;
 | 
				
			||||||
			if (destinationDockerId) {
 | 
								if (destinationDockerId) {
 | 
				
			||||||
				await startTcpProxy(destinationDocker, `${id}-ftp`, ftpPublicPort, 22);
 | 
									if (destinationDocker.isCoolifyProxyUsed) {
 | 
				
			||||||
 | 
										if (settings.isTraefikUsed) {
 | 
				
			||||||
 | 
											await stopTcpHttpProxy(
 | 
				
			||||||
 | 
												id,
 | 
				
			||||||
 | 
												destinationDocker,
 | 
				
			||||||
 | 
												ftpPublicPort,
 | 
				
			||||||
 | 
												`haproxy-for-${ftpPublicPort}`
 | 
				
			||||||
 | 
											);
 | 
				
			||||||
 | 
											await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp');
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											await stopTcpHttpProxy(id, destinationDocker, ftpPublicPort, `${id}-${ftpPublicPort}`);
 | 
				
			||||||
 | 
											await startTcpProxy(destinationDocker, `${id}-ftp`, ftpPublicPort, 22);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// HTTP Proxies
 | 
							// HTTP Proxies
 | 
				
			||||||
		const minioInstances = await prisma.minio.findMany({
 | 
							if (!settings.isTraefikUsed) {
 | 
				
			||||||
			where: { publicPort: { not: null } },
 | 
								const minioInstances = await prisma.minio.findMany({
 | 
				
			||||||
			include: { service: { include: { destinationDocker: true } } }
 | 
									where: { publicPort: { not: null } },
 | 
				
			||||||
		});
 | 
									include: { service: { include: { destinationDocker: true } } }
 | 
				
			||||||
		for (const minio of minioInstances) {
 | 
								});
 | 
				
			||||||
			const { service, publicPort } = minio;
 | 
								for (const minio of minioInstances) {
 | 
				
			||||||
			const { destinationDockerId, destinationDocker, id } = service;
 | 
									const { service, publicPort } = minio;
 | 
				
			||||||
			if (destinationDockerId) {
 | 
									const { destinationDockerId, destinationDocker, id } = service;
 | 
				
			||||||
				await startHttpProxy(destinationDocker, id, publicPort, 9000);
 | 
									if (destinationDockerId) {
 | 
				
			||||||
 | 
										if (destinationDocker.isCoolifyProxyUsed) {
 | 
				
			||||||
 | 
											await stopTcpHttpProxy(id, destinationDocker, publicPort, `${id}-${publicPort}`);
 | 
				
			||||||
 | 
											await startHttpProxy(destinationDocker, id, publicPort, 9000);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,12 @@
 | 
				
			|||||||
import { generateSSLCerts } from '$lib/letsencrypt';
 | 
					import { generateSSLCerts } from '$lib/letsencrypt';
 | 
				
			||||||
 | 
					import { prisma } from '$lib/database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function (): Promise<void> {
 | 
					export default async function (): Promise<void> {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		return await generateSSLCerts();
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
 | 
							if (!settings.isTraefikUsed) {
 | 
				
			||||||
 | 
								return await generateSSLCerts();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		console.log(error);
 | 
							console.log(error);
 | 
				
			||||||
		throw error;
 | 
							throw error;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,14 @@
 | 
				
			|||||||
import { asyncExecShell } from '$lib/common';
 | 
					import { renewSSLCerts } from '$lib/letsencrypt';
 | 
				
			||||||
import { reloadHaproxy } from '$lib/haproxy';
 | 
					import { prisma } from '$lib/database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function (): Promise<void> {
 | 
					export default async function (): Promise<void> {
 | 
				
			||||||
	await asyncExecShell(
 | 
						try {
 | 
				
			||||||
		`docker run --rm --name certbot-renewal -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs renew`
 | 
							const settings = await prisma.setting.findFirst();
 | 
				
			||||||
	);
 | 
							if (!settings.isTraefikUsed) {
 | 
				
			||||||
	await reloadHaproxy('unix:///var/run/docker.sock');
 | 
								return await renewSSLCerts();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(error);
 | 
				
			||||||
 | 
							throw error;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								src/lib/realtime.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/lib/realtime.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					// import ioClient from 'socket.io-client';
 | 
				
			||||||
 | 
					// const socket = ioClient('http://localhost:3000');
 | 
				
			||||||
 | 
					// export const io = socket;
 | 
				
			||||||
@@ -12,3 +12,14 @@ export const features: Readable<{ latestVersion: string; beta: boolean }> = read
 | 
				
			|||||||
	beta: browser && window.localStorage.getItem('beta') === 'true',
 | 
						beta: browser && window.localStorage.getItem('beta') === 'true',
 | 
				
			||||||
	latestVersion: browser && window.localStorage.getItem('latestVersion')
 | 
						latestVersion: browser && window.localStorage.getItem('latestVersion')
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const isTraefikUsed: Writable<boolean> = writable(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const status: Writable<any> = writable({
 | 
				
			||||||
 | 
						application: {
 | 
				
			||||||
 | 
							isRunning: false,
 | 
				
			||||||
 | 
							isExited: false,
 | 
				
			||||||
 | 
							loading: false,
 | 
				
			||||||
 | 
							initialLoading: true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ export type BuilderJob = {
 | 
				
			|||||||
	buildPack: BuildPackName;
 | 
						buildPack: BuildPackName;
 | 
				
			||||||
	projectId: number;
 | 
						projectId: number;
 | 
				
			||||||
	port: number;
 | 
						port: number;
 | 
				
			||||||
 | 
						exposePort?: number;
 | 
				
			||||||
	installCommand: string;
 | 
						installCommand: string;
 | 
				
			||||||
	buildCommand?: string;
 | 
						buildCommand?: string;
 | 
				
			||||||
	startCommand?: string;
 | 
						startCommand?: string;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ export type ComposeFileService = {
 | 
				
			|||||||
	restart: ComposeFileRestartOption;
 | 
						restart: ComposeFileRestartOption;
 | 
				
			||||||
	depends_on?: string[];
 | 
						depends_on?: string[];
 | 
				
			||||||
	command?: string;
 | 
						command?: string;
 | 
				
			||||||
 | 
						ports?: string[];
 | 
				
			||||||
	build?: {
 | 
						build?: {
 | 
				
			||||||
		context: string;
 | 
							context: string;
 | 
				
			||||||
		dockerfile: string;
 | 
							dockerfile: string;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								src/routes/_Trend.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/routes/_Trend.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						export let trend;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if trend === 'up'}
 | 
				
			||||||
 | 
						<span class="-mt-1 inline-flex px-2 text-green-500">
 | 
				
			||||||
 | 
							<svg
 | 
				
			||||||
 | 
								xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
								class="h-8 w-8"
 | 
				
			||||||
 | 
								viewBox="0 0 24 24"
 | 
				
			||||||
 | 
								stroke-width="1.5"
 | 
				
			||||||
 | 
								stroke="currentColor"
 | 
				
			||||||
 | 
								fill="none"
 | 
				
			||||||
 | 
								stroke-linecap="round"
 | 
				
			||||||
 | 
								stroke-linejoin="round"
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
								<line x1="17" y1="7" x2="7" y2="17" />
 | 
				
			||||||
 | 
								<polyline points="8 7 17 7 17 16" />
 | 
				
			||||||
 | 
							</svg></span
 | 
				
			||||||
 | 
						>
 | 
				
			||||||
 | 
					{:else if trend === 'down'}
 | 
				
			||||||
 | 
						<span class="text-red-500 px-2 inline-flex -mt-1">
 | 
				
			||||||
 | 
							<svg
 | 
				
			||||||
 | 
								xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
								class="w-8 h-8"
 | 
				
			||||||
 | 
								viewBox="0 0 24 24"
 | 
				
			||||||
 | 
								stroke-width="1.5"
 | 
				
			||||||
 | 
								stroke="currentColor"
 | 
				
			||||||
 | 
								fill="none"
 | 
				
			||||||
 | 
								stroke-linecap="round"
 | 
				
			||||||
 | 
								stroke-linejoin="round"
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
								<line x1="7" y1="7" x2="17" y2="17" />
 | 
				
			||||||
 | 
								<polyline points="17 8 17 17 8 17" />
 | 
				
			||||||
 | 
							</svg>
 | 
				
			||||||
 | 
						</span>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
@@ -34,23 +34,30 @@
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
						export let settings;
 | 
				
			||||||
	import '../tailwind.css';
 | 
						import '../tailwind.css';
 | 
				
			||||||
	import { SvelteToast, toast } from '@zerodevx/svelte-toast';
 | 
						import { SvelteToast, toast } from '@zerodevx/svelte-toast';
 | 
				
			||||||
	import { page, session } from '$app/stores';
 | 
						import { page, session } from '$app/stores';
 | 
				
			||||||
 | 
						import { fade } from 'svelte/transition';
 | 
				
			||||||
	import { onMount } from 'svelte';
 | 
						import { onMount } from 'svelte';
 | 
				
			||||||
	import { errorNotification } from '$lib/form';
 | 
						import { errorNotification } from '$lib/form';
 | 
				
			||||||
	import { asyncSleep } from '$lib/components/common';
 | 
						import { asyncSleep } from '$lib/components/common';
 | 
				
			||||||
	import { del, get, post } from '$lib/api';
 | 
						import { del, get, post } from '$lib/api';
 | 
				
			||||||
	import { dev } from '$app/env';
 | 
						import { dev } from '$app/env';
 | 
				
			||||||
	import { features } from '$lib/store';
 | 
						import { features, isTraefikUsed } from '$lib/store';
 | 
				
			||||||
	let isUpdateAvailable = false;
 | 
						import { navigating } from '$app/stores';
 | 
				
			||||||
 | 
						import PageLoader from '$lib/components/PageLoader.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$isTraefikUsed = settings?.isTraefikUsed || false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let isUpdateAvailable = false;
 | 
				
			||||||
	let updateStatus = {
 | 
						let updateStatus = {
 | 
				
			||||||
		found: false,
 | 
							found: false,
 | 
				
			||||||
		loading: false,
 | 
							loading: false,
 | 
				
			||||||
		success: null
 | 
							success: null
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	let latestVersion = 'latest';
 | 
						let latestVersion = 'latest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onMount(async () => {
 | 
						onMount(async () => {
 | 
				
			||||||
		if ($session.userId) {
 | 
							if ($session.userId) {
 | 
				
			||||||
			const overrideVersion = $features.latestVersion;
 | 
								const overrideVersion = $features.latestVersion;
 | 
				
			||||||
@@ -78,6 +85,7 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function logout() {
 | 
						async function logout() {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			await del(`/logout.json`, {});
 | 
								await del(`/logout.json`, {});
 | 
				
			||||||
@@ -128,16 +136,23 @@
 | 
				
			|||||||
	<title>Coolify</title>
 | 
						<title>Coolify</title>
 | 
				
			||||||
	{#if !$session.whiteLabeled}
 | 
						{#if !$session.whiteLabeled}
 | 
				
			||||||
		<link rel="icon" href="/favicon.png" />
 | 
							<link rel="icon" href="/favicon.png" />
 | 
				
			||||||
 | 
						{:else if $session.whiteLabelDetails.icon}
 | 
				
			||||||
 | 
							<link rel="icon" href={$session.whiteLabelDetails.icon} />
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
</svelte:head>
 | 
					</svelte:head>
 | 
				
			||||||
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
 | 
					<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
 | 
				
			||||||
 | 
					{#if $navigating}
 | 
				
			||||||
 | 
						<div out:fade={{ delay: 100 }}>
 | 
				
			||||||
 | 
							<PageLoader />
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
{#if $session.userId}
 | 
					{#if $session.userId}
 | 
				
			||||||
	<nav class="nav-main">
 | 
						<nav class="nav-main">
 | 
				
			||||||
		<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
 | 
							<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
 | 
				
			||||||
			{#if !$session.whiteLabeled}
 | 
								{#if !$session.whiteLabeled}
 | 
				
			||||||
				<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
 | 
									<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
 | 
				
			||||||
			{/if}
 | 
								{/if}
 | 
				
			||||||
			<div class="flex flex-col space-y-4 py-2" class:mt-2={$session.whiteLabeled}>
 | 
								<div class="flex flex-col space-y-2 py-2" class:mt-2={$session.whiteLabeled}>
 | 
				
			||||||
				<a
 | 
									<a
 | 
				
			||||||
					sveltekit:prefetch
 | 
										sveltekit:prefetch
 | 
				
			||||||
					href="/"
 | 
										href="/"
 | 
				
			||||||
@@ -222,7 +237,6 @@
 | 
				
			|||||||
						<polyline points="10 15 13 18 10 21" />
 | 
											<polyline points="10 15 13 18 10 21" />
 | 
				
			||||||
					</svg>
 | 
										</svg>
 | 
				
			||||||
				</a>
 | 
									</a>
 | 
				
			||||||
				<div class="border-t border-stone-700" />
 | 
					 | 
				
			||||||
				<a
 | 
									<a
 | 
				
			||||||
					sveltekit:prefetch
 | 
										sveltekit:prefetch
 | 
				
			||||||
					href="/destinations"
 | 
										href="/destinations"
 | 
				
			||||||
@@ -284,7 +298,6 @@
 | 
				
			|||||||
						<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
 | 
											<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
 | 
				
			||||||
					</svg>
 | 
										</svg>
 | 
				
			||||||
				</a>
 | 
									</a>
 | 
				
			||||||
				<div class="border-t border-stone-700" />
 | 
					 | 
				
			||||||
				<a
 | 
									<a
 | 
				
			||||||
					sveltekit:prefetch
 | 
										sveltekit:prefetch
 | 
				
			||||||
					href="/services"
 | 
										href="/services"
 | 
				
			||||||
@@ -423,7 +436,7 @@
 | 
				
			|||||||
					{/if}
 | 
										{/if}
 | 
				
			||||||
				{/if}
 | 
									{/if}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="flex flex-col space-y-4 py-2">
 | 
								<div class="flex flex-col space-y-2 py-2">
 | 
				
			||||||
				<a
 | 
									<a
 | 
				
			||||||
					sveltekit:prefetch
 | 
										sveltekit:prefetch
 | 
				
			||||||
					href="/iam"
 | 
										href="/iam"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
		const endpoint = `/applications/${params.id}.json`;
 | 
							const endpoint = `/applications/${params.id}.json`;
 | 
				
			||||||
		const res = await fetch(endpoint);
 | 
							const res = await fetch(endpoint);
 | 
				
			||||||
		if (res.ok) {
 | 
							if (res.ok) {
 | 
				
			||||||
			let { application, isRunning, isExited, appId, githubToken, gitlabToken } = await res.json();
 | 
								let { application, appId, githubToken, gitlabToken } = await res.json();
 | 
				
			||||||
			if (!application || Object.entries(application).length === 0) {
 | 
								if (!application || Object.entries(application).length === 0) {
 | 
				
			||||||
				return {
 | 
									return {
 | 
				
			||||||
					status: 302,
 | 
										status: 302,
 | 
				
			||||||
@@ -45,13 +45,10 @@
 | 
				
			|||||||
			return {
 | 
								return {
 | 
				
			||||||
				props: {
 | 
									props: {
 | 
				
			||||||
					application,
 | 
										application,
 | 
				
			||||||
					isRunning,
 | 
					 | 
				
			||||||
					isExited,
 | 
					 | 
				
			||||||
					githubToken,
 | 
										githubToken,
 | 
				
			||||||
					gitlabToken
 | 
										gitlabToken
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				stuff: {
 | 
									stuff: {
 | 
				
			||||||
					isRunning,
 | 
					 | 
				
			||||||
					application,
 | 
										application,
 | 
				
			||||||
					appId
 | 
										appId
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -67,8 +64,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	export let application;
 | 
						export let application;
 | 
				
			||||||
	export let isRunning;
 | 
					 | 
				
			||||||
	export let isExited;
 | 
					 | 
				
			||||||
	export let githubToken;
 | 
						export let githubToken;
 | 
				
			||||||
	export let gitlabToken;
 | 
						export let gitlabToken;
 | 
				
			||||||
	import { page, session } from '$app/stores';
 | 
						import { page, session } from '$app/stores';
 | 
				
			||||||
@@ -77,7 +72,7 @@
 | 
				
			|||||||
	import Loading from '$lib/components/Loading.svelte';
 | 
						import Loading from '$lib/components/Loading.svelte';
 | 
				
			||||||
	import { del, get, post } from '$lib/api';
 | 
						import { del, get, post } from '$lib/api';
 | 
				
			||||||
	import { goto } from '$app/navigation';
 | 
						import { goto } from '$app/navigation';
 | 
				
			||||||
	import { gitTokens } from '$lib/store';
 | 
						import { gitTokens, status } from '$lib/store';
 | 
				
			||||||
	import { toast } from '@zerodevx/svelte-toast';
 | 
						import { toast } from '@zerodevx/svelte-toast';
 | 
				
			||||||
	import { disabledButton } from '$lib/store';
 | 
						import { disabledButton } from '$lib/store';
 | 
				
			||||||
	import { onDestroy, onMount } from 'svelte';
 | 
						import { onDestroy, onMount } from 'svelte';
 | 
				
			||||||
@@ -135,17 +130,31 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	async function getStatus() {
 | 
						async function getStatus() {
 | 
				
			||||||
		statusInterval = setInterval(async () => {
 | 
							if ($status.application.loading) return;
 | 
				
			||||||
			const data = await get(`/applications/${id}.json`);
 | 
							$status.application.loading = true;
 | 
				
			||||||
			isRunning = data.isRunning;
 | 
							const data = await get(`/applications/${id}/status.json`);
 | 
				
			||||||
			isExited = data.isExited;
 | 
							$status.application.isRunning = data.isRunning;
 | 
				
			||||||
		}, 1000);
 | 
							$status.application.isExited = data.isExited;
 | 
				
			||||||
 | 
							$status.application.loading = false;
 | 
				
			||||||
 | 
							$status.application.initialLoading = false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	onDestroy(() => {
 | 
						onDestroy(() => {
 | 
				
			||||||
 | 
							$status.application.initialLoading = true;
 | 
				
			||||||
		clearInterval(statusInterval);
 | 
							clearInterval(statusInterval);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	onMount(async () => {
 | 
						onMount(async () => {
 | 
				
			||||||
		await getStatus();
 | 
							if (!application.gitSourceId || !application.destinationDockerId || !application.fqdn) {
 | 
				
			||||||
 | 
								$status.application.initialLoading = false;
 | 
				
			||||||
 | 
								$status.application.isRunning = false;
 | 
				
			||||||
 | 
								$status.application.isExited = false;
 | 
				
			||||||
 | 
								$status.application.loading = false;
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await getStatus();
 | 
				
			||||||
 | 
								statusInterval = setInterval(async () => {
 | 
				
			||||||
 | 
									await getStatus();
 | 
				
			||||||
 | 
								}, 1000);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -153,16 +162,16 @@
 | 
				
			|||||||
	{#if loading}
 | 
						{#if loading}
 | 
				
			||||||
		<Loading fullscreen cover />
 | 
							<Loading fullscreen cover />
 | 
				
			||||||
	{:else}
 | 
						{:else}
 | 
				
			||||||
		{#if isExited}
 | 
							{#if $status.application.isExited}
 | 
				
			||||||
			<a
 | 
								<a
 | 
				
			||||||
				href={!$disabledButton ? `/applications/${id}/logs` : null}
 | 
									href={!$disabledButton ? `/applications/${id}/logs` : null}
 | 
				
			||||||
				class=" icons bg-transparent tooltip-bottom text-sm flex items-center text-red-500 tooltip-red-500"
 | 
									class=" icons tooltip-bottom tooltip-red-500 flex items-center bg-transparent text-sm text-red-500"
 | 
				
			||||||
				data-tooltip="Application exited with an error!"
 | 
									data-tooltip="Application exited with an error!"
 | 
				
			||||||
				sveltekit:prefetch
 | 
									sveltekit:prefetch
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
					xmlns="http://www.w3.org/2000/svg"
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
					class="w-6 h-6"
 | 
										class="h-6 w-6"
 | 
				
			||||||
					viewBox="0 0 24 24"
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
					stroke-width="1.5"
 | 
										stroke-width="1.5"
 | 
				
			||||||
					stroke="currentcolor"
 | 
										stroke="currentcolor"
 | 
				
			||||||
@@ -179,20 +188,43 @@
 | 
				
			|||||||
				</svg>
 | 
									</svg>
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
		{/if}
 | 
							{/if}
 | 
				
			||||||
		{#if isRunning}
 | 
							{#if $status.application.initialLoading}
 | 
				
			||||||
 | 
								<button
 | 
				
			||||||
 | 
									class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<svg
 | 
				
			||||||
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
										class="h-6 w-6"
 | 
				
			||||||
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
 | 
										stroke-width="1.5"
 | 
				
			||||||
 | 
										stroke="currentColor"
 | 
				
			||||||
 | 
										fill="none"
 | 
				
			||||||
 | 
										stroke-linecap="round"
 | 
				
			||||||
 | 
										stroke-linejoin="round"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
										<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
 | 
				
			||||||
 | 
										<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
 | 
				
			||||||
 | 
										<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
 | 
				
			||||||
 | 
										<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
 | 
				
			||||||
 | 
										<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
 | 
				
			||||||
 | 
										<line x1="11" y1="19.94" x2="11" y2="19.95" />
 | 
				
			||||||
 | 
									</svg>
 | 
				
			||||||
 | 
								</button>
 | 
				
			||||||
 | 
							{:else if $status.application.isRunning}
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				on:click={stopApplication}
 | 
									on:click={stopApplication}
 | 
				
			||||||
				title="Stop application"
 | 
									title="Stop application"
 | 
				
			||||||
				type="submit"
 | 
									type="submit"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
 | 
									class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm text-red-500"
 | 
				
			||||||
				data-tooltip={$session.isAdmin
 | 
									data-tooltip={$session.isAdmin
 | 
				
			||||||
					? $t('application.stop_application')
 | 
										? $t('application.stop_application')
 | 
				
			||||||
					: $t('application.permission_denied_stop_application')}
 | 
										: $t('application.permission_denied_stop_application')}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
					xmlns="http://www.w3.org/2000/svg"
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
					class="w-6 h-6"
 | 
										class="h-6 w-6"
 | 
				
			||||||
					viewBox="0 0 24 24"
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
					stroke-width="1.5"
 | 
										stroke-width="1.5"
 | 
				
			||||||
					stroke="currentColor"
 | 
										stroke="currentColor"
 | 
				
			||||||
@@ -210,14 +242,14 @@
 | 
				
			|||||||
					title="Rebuild application"
 | 
										title="Rebuild application"
 | 
				
			||||||
					type="submit"
 | 
										type="submit"
 | 
				
			||||||
					disabled={$disabledButton}
 | 
										disabled={$disabledButton}
 | 
				
			||||||
					class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:text-green-500"
 | 
										class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm hover:text-green-500"
 | 
				
			||||||
					data-tooltip={$session.isAdmin
 | 
										data-tooltip={$session.isAdmin
 | 
				
			||||||
						? 'Rebuild application'
 | 
											? 'Rebuild application'
 | 
				
			||||||
						: 'You do not have permission to rebuild application.'}
 | 
											: 'You do not have permission to rebuild application.'}
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
					<svg
 | 
										<svg
 | 
				
			||||||
						xmlns="http://www.w3.org/2000/svg"
 | 
											xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
						class="w-6 h-6"
 | 
											class="h-6 w-6"
 | 
				
			||||||
						viewBox="0 0 24 24"
 | 
											viewBox="0 0 24 24"
 | 
				
			||||||
						stroke-width="1.5"
 | 
											stroke-width="1.5"
 | 
				
			||||||
						stroke="currentColor"
 | 
											stroke="currentColor"
 | 
				
			||||||
@@ -239,14 +271,14 @@
 | 
				
			|||||||
					title="Build and start application"
 | 
										title="Build and start application"
 | 
				
			||||||
					type="submit"
 | 
										type="submit"
 | 
				
			||||||
					disabled={$disabledButton}
 | 
										disabled={$disabledButton}
 | 
				
			||||||
					class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
 | 
										class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm text-green-500"
 | 
				
			||||||
					data-tooltip={$session.isAdmin
 | 
										data-tooltip={$session.isAdmin
 | 
				
			||||||
						? 'Build and start application'
 | 
											? 'Build and start application'
 | 
				
			||||||
						: 'You do not have permission to Build and start application.'}
 | 
											: 'You do not have permission to Build and start application.'}
 | 
				
			||||||
				>
 | 
									>
 | 
				
			||||||
					<svg
 | 
										<svg
 | 
				
			||||||
						xmlns="http://www.w3.org/2000/svg"
 | 
											xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
						class="w-6 h-6"
 | 
											class="h-6 w-6"
 | 
				
			||||||
						viewBox="0 0 24 24"
 | 
											viewBox="0 0 24 24"
 | 
				
			||||||
						stroke-width="1.5"
 | 
											stroke-width="1.5"
 | 
				
			||||||
						stroke="currentColor"
 | 
											stroke="currentColor"
 | 
				
			||||||
@@ -261,18 +293,18 @@
 | 
				
			|||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		{/if}
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="border border-coolgray-500 h-8" />
 | 
							<div class="h-8 border border-coolgray-500" />
 | 
				
			||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton ? `/applications/${id}` : null}
 | 
								href={!$disabledButton ? `/applications/${id}` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-yellow-500 rounded"
 | 
								class="rounded hover:text-yellow-500"
 | 
				
			||||||
			class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
 | 
								class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title="Configurations"
 | 
									title="Configurations"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip="Configurations"
 | 
									data-tooltip="Configurations"
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
@@ -301,19 +333,19 @@
 | 
				
			|||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton ? `/applications/${id}/secrets` : null}
 | 
								href={!$disabledButton ? `/applications/${id}/secrets` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-pink-500 rounded"
 | 
								class="rounded hover:text-pink-500"
 | 
				
			||||||
			class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
 | 
								class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title="Secret"
 | 
									title="Secret"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip="Secret"
 | 
									data-tooltip="Secret"
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
					xmlns="http://www.w3.org/2000/svg"
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
					class="w-6 h-6"
 | 
										class="h-6 w-6"
 | 
				
			||||||
					viewBox="0 0 24 24"
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
					stroke-width="1.5"
 | 
										stroke-width="1.5"
 | 
				
			||||||
					stroke="currentColor"
 | 
										stroke="currentColor"
 | 
				
			||||||
@@ -333,19 +365,19 @@
 | 
				
			|||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton ? `/applications/${id}/storage` : null}
 | 
								href={!$disabledButton ? `/applications/${id}/storage` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-pink-500 rounded"
 | 
								class="rounded hover:text-pink-500"
 | 
				
			||||||
			class:text-pink-500={$page.url.pathname === `/applications/${id}/storage`}
 | 
								class:text-pink-500={$page.url.pathname === `/applications/${id}/storage`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storage`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storage`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title="Persistent Storage"
 | 
									title="Persistent Storage"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip="Persistent Storage"
 | 
									data-tooltip="Persistent Storage"
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
					xmlns="http://www.w3.org/2000/svg"
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
					class="w-6 h-6"
 | 
										class="h-6 w-6"
 | 
				
			||||||
					viewBox="0 0 24 24"
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
					stroke-width="1.5"
 | 
										stroke-width="1.5"
 | 
				
			||||||
					stroke="currentColor"
 | 
										stroke="currentColor"
 | 
				
			||||||
@@ -363,19 +395,19 @@
 | 
				
			|||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton ? `/applications/${id}/previews` : null}
 | 
								href={!$disabledButton ? `/applications/${id}/previews` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-orange-500 rounded"
 | 
								class="rounded hover:text-orange-500"
 | 
				
			||||||
			class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
 | 
								class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title="Previews"
 | 
									title="Previews"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip="Previews"
 | 
									data-tooltip="Previews"
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
					xmlns="http://www.w3.org/2000/svg"
 | 
										xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
					class="w-6 h-6"
 | 
										class="h-6 w-6"
 | 
				
			||||||
					viewBox="0 0 24 24"
 | 
										viewBox="0 0 24 24"
 | 
				
			||||||
					stroke-width="1.5"
 | 
										stroke-width="1.5"
 | 
				
			||||||
					stroke="currentColor"
 | 
										stroke="currentColor"
 | 
				
			||||||
@@ -392,18 +424,18 @@
 | 
				
			|||||||
				</svg></button
 | 
									</svg></button
 | 
				
			||||||
			></a
 | 
								></a
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
		<div class="border border-coolgray-500 h-8" />
 | 
							<div class="h-8 border border-coolgray-500" />
 | 
				
			||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton && isRunning ? `/applications/${id}/logs` : null}
 | 
								href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-sky-500 rounded"
 | 
								class="rounded hover:text-sky-500"
 | 
				
			||||||
			class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
 | 
								class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title={$t('application.logs')}
 | 
									title={$t('application.logs')}
 | 
				
			||||||
				disabled={$disabledButton || !isRunning}
 | 
									disabled={$disabledButton || !$status.application.isRunning}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip={$t('application.logs')}
 | 
									data-tooltip={$t('application.logs')}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
@@ -428,14 +460,14 @@
 | 
				
			|||||||
		<a
 | 
							<a
 | 
				
			||||||
			href={!$disabledButton ? `/applications/${id}/logs/build` : null}
 | 
								href={!$disabledButton ? `/applications/${id}/logs/build` : null}
 | 
				
			||||||
			sveltekit:prefetch
 | 
								sveltekit:prefetch
 | 
				
			||||||
			class="hover:text-red-500 rounded"
 | 
								class="rounded hover:text-red-500"
 | 
				
			||||||
			class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
 | 
								class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
 | 
				
			||||||
			class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
 | 
								class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<button
 | 
								<button
 | 
				
			||||||
				title="Build Logs"
 | 
									title="Build Logs"
 | 
				
			||||||
				disabled={$disabledButton}
 | 
									disabled={$disabledButton}
 | 
				
			||||||
				class="icons bg-transparent tooltip-bottom text-sm"
 | 
									class="icons tooltip-bottom bg-transparent text-sm"
 | 
				
			||||||
				data-tooltip="Build Logs"
 | 
									data-tooltip="Build Logs"
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<svg
 | 
									<svg
 | 
				
			||||||
@@ -460,7 +492,7 @@
 | 
				
			|||||||
				</svg>
 | 
									</svg>
 | 
				
			||||||
			</button></a
 | 
								</button></a
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
		<div class="border border-coolgray-500 h-8" />
 | 
							<div class="h-8 border border-coolgray-500" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<button
 | 
							<button
 | 
				
			||||||
			on:click={() => deleteApplication(application.name)}
 | 
								on:click={() => deleteApplication(application.name)}
 | 
				
			||||||
@@ -468,7 +500,7 @@
 | 
				
			|||||||
			type="submit"
 | 
								type="submit"
 | 
				
			||||||
			disabled={!$session.isAdmin}
 | 
								disabled={!$session.isAdmin}
 | 
				
			||||||
			class:hover:text-red-500={$session.isAdmin}
 | 
								class:hover:text-red-500={$session.isAdmin}
 | 
				
			||||||
			class="icons bg-transparent  tooltip-bottom text-sm"
 | 
								class="icons tooltip-bottom  bg-transparent text-sm"
 | 
				
			||||||
			data-tooltip={$session.isAdmin
 | 
								data-tooltip={$session.isAdmin
 | 
				
			||||||
				? $t('application.delete_application')
 | 
									? $t('application.delete_application')
 | 
				
			||||||
				: $t('application.permission_denied_delete_application')}
 | 
									: $t('application.permission_denied_delete_application')}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,61 @@ import { buildQueue } from '$lib/queues';
 | 
				
			|||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function cleanupDB(buildId: string) {
 | 
				
			||||||
 | 
						const data = await db.prisma.build.findUnique({ where: { id: buildId } });
 | 
				
			||||||
 | 
						if (data?.status === 'queued' || data?.status === 'running') {
 | 
				
			||||||
 | 
							await db.prisma.build.update({ where: { id: buildId }, data: { status: 'failed' } });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function stopBuild(buildId, applicationId) {
 | 
				
			||||||
 | 
						let count = 0;
 | 
				
			||||||
 | 
						await new Promise<void>(async (resolve, reject) => {
 | 
				
			||||||
 | 
							const job = await buildQueue.getJob(buildId);
 | 
				
			||||||
 | 
							if (!job) {
 | 
				
			||||||
 | 
								await cleanupDB(buildId);
 | 
				
			||||||
 | 
								return resolve();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							const {
 | 
				
			||||||
 | 
								destinationDocker: { engine }
 | 
				
			||||||
 | 
							} = job?.data;
 | 
				
			||||||
 | 
							const host = getEngine(engine);
 | 
				
			||||||
 | 
							let interval = setInterval(async () => {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									const data = await db.prisma.build.findUnique({ where: { id: buildId } });
 | 
				
			||||||
 | 
									if (data?.status === 'failed') {
 | 
				
			||||||
 | 
										clearInterval(interval);
 | 
				
			||||||
 | 
										return resolve();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (count > 100) {
 | 
				
			||||||
 | 
										clearInterval(interval);
 | 
				
			||||||
 | 
										return reject(new Error('Build canceled'));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const { stdout: buildContainers } = await asyncExecShell(
 | 
				
			||||||
 | 
										`DOCKER_HOST=${host} docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'`
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									if (buildContainers) {
 | 
				
			||||||
 | 
										const containersArray = buildContainers.trim().split('\n');
 | 
				
			||||||
 | 
										for (const container of containersArray) {
 | 
				
			||||||
 | 
											const containerObj = JSON.parse(container);
 | 
				
			||||||
 | 
											const id = containerObj.ID;
 | 
				
			||||||
 | 
											if (!containerObj.Names.startsWith(`${applicationId}`)) {
 | 
				
			||||||
 | 
												await removeDestinationDocker({ id, engine });
 | 
				
			||||||
 | 
												clearInterval(interval);
 | 
				
			||||||
 | 
												await saveBuildLog({
 | 
				
			||||||
 | 
													line: 'Canceled by user!',
 | 
				
			||||||
 | 
													buildId: job.data.build_id,
 | 
				
			||||||
 | 
													applicationId: job.data.id
 | 
				
			||||||
 | 
												});
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									count++;
 | 
				
			||||||
 | 
								} catch (error) {}
 | 
				
			||||||
 | 
							}, 100);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
	const { buildId, applicationId } = await event.request.json();
 | 
						const { buildId, applicationId } = await event.request.json();
 | 
				
			||||||
	if (!buildId) {
 | 
						if (!buildId) {
 | 
				
			||||||
@@ -14,50 +69,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		let count = 0;
 | 
							await stopBuild(buildId, applicationId);
 | 
				
			||||||
		await new Promise<void>(async (resolve, reject) => {
 | 
					 | 
				
			||||||
			const job = await buildQueue.getJob(buildId);
 | 
					 | 
				
			||||||
			const {
 | 
					 | 
				
			||||||
				destinationDocker: { engine }
 | 
					 | 
				
			||||||
			} = job.data;
 | 
					 | 
				
			||||||
			const host = getEngine(engine);
 | 
					 | 
				
			||||||
			let interval = setInterval(async () => {
 | 
					 | 
				
			||||||
				const { status } = await db.prisma.build.findUnique({ where: { id: buildId } });
 | 
					 | 
				
			||||||
				if (status === 'failed') {
 | 
					 | 
				
			||||||
					clearInterval(interval);
 | 
					 | 
				
			||||||
					return resolve();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if (count > 1200) {
 | 
					 | 
				
			||||||
					clearInterval(interval);
 | 
					 | 
				
			||||||
					reject(new Error('Could not cancel build.'));
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				try {
 | 
					 | 
				
			||||||
					const { stdout: buildContainers } = await asyncExecShell(
 | 
					 | 
				
			||||||
						`DOCKER_HOST=${host} docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'`
 | 
					 | 
				
			||||||
					);
 | 
					 | 
				
			||||||
					if (buildContainers) {
 | 
					 | 
				
			||||||
						const containersArray = buildContainers.trim().split('\n');
 | 
					 | 
				
			||||||
						for (const container of containersArray) {
 | 
					 | 
				
			||||||
							const containerObj = JSON.parse(container);
 | 
					 | 
				
			||||||
							const id = containerObj.ID;
 | 
					 | 
				
			||||||
							if (!containerObj.Names.startsWith(`${applicationId}`)) {
 | 
					 | 
				
			||||||
								await removeDestinationDocker({ id, engine });
 | 
					 | 
				
			||||||
								clearInterval(interval);
 | 
					 | 
				
			||||||
								await saveBuildLog({
 | 
					 | 
				
			||||||
									line: 'Canceled by user!',
 | 
					 | 
				
			||||||
									buildId: job.data.build_id,
 | 
					 | 
				
			||||||
									applicationId: job.data.id
 | 
					 | 
				
			||||||
								});
 | 
					 | 
				
			||||||
							}
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					count++;
 | 
					 | 
				
			||||||
				} catch (error) {}
 | 
					 | 
				
			||||||
			}, 100);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			resolve();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200,
 | 
								status: 200,
 | 
				
			||||||
			body: {
 | 
								body: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,44 @@
 | 
				
			|||||||
import { dev } from '$app/env';
 | 
					import { dev } from '$app/env';
 | 
				
			||||||
import { getDomain, getUserDetails } from '$lib/common';
 | 
					import { checkDomainsIsValidInDNS, getDomain, getUserDetails, isDNSValid } from '$lib/common';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
import { promises as dns } from 'dns';
 | 
					import { promises as dns } from 'dns';
 | 
				
			||||||
 | 
					import getPort from 'get-port';
 | 
				
			||||||
import { t } from '$lib/translations';
 | 
					import { t } from '$lib/translations';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
						const domain = event.url.searchParams.get('domain');
 | 
				
			||||||
 | 
						if (!domain) {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 500,
 | 
				
			||||||
 | 
								body: {
 | 
				
			||||||
 | 
									message: t.get('application.domain_required')
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							await isDNSValid(event, domain);
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 200
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
	const { status, body } = await getUserDetails(event);
 | 
						const { status, body } = await getUserDetails(event);
 | 
				
			||||||
	if (status === 401) return { status, body };
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { id } = event.params;
 | 
						const { id } = event.params;
 | 
				
			||||||
	let { fqdn, forceSave } = await event.request.json();
 | 
						let { exposePort, fqdn, forceSave, dualCerts } = await event.request.json();
 | 
				
			||||||
	fqdn = fqdn.toLowerCase();
 | 
						fqdn = fqdn.toLowerCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const domain = getDomain(fqdn);
 | 
							const { isDNSCheckEnabled } = await db.prisma.setting.findFirst({});
 | 
				
			||||||
		const found = await db.isDomainConfigured({ id, fqdn });
 | 
							const found = await db.isDomainConfigured({ id, fqdn });
 | 
				
			||||||
		if (found) {
 | 
							if (found) {
 | 
				
			||||||
			throw {
 | 
								throw {
 | 
				
			||||||
@@ -24,25 +47,22 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
				})
 | 
									})
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!dev && !forceSave) {
 | 
					 | 
				
			||||||
			let ip = [];
 | 
					 | 
				
			||||||
			let localIp = [];
 | 
					 | 
				
			||||||
			dns.setServers(['1.1.1.1', '8.8.8.8']);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			try {
 | 
							if (exposePort) {
 | 
				
			||||||
				localIp = await dns.resolve4(event.url.hostname);
 | 
								exposePort = Number(exposePort);
 | 
				
			||||||
			} catch (error) {}
 | 
					 | 
				
			||||||
			try {
 | 
					 | 
				
			||||||
				ip = await dns.resolve4(domain);
 | 
					 | 
				
			||||||
			} catch (error) {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (localIp?.length > 0) {
 | 
								if (exposePort < 1024 || exposePort > 65535) {
 | 
				
			||||||
				if (ip?.length === 0 || !ip.includes(localIp[0])) {
 | 
									throw { message: `Exposed Port needs to be between 1024 and 65535.` };
 | 
				
			||||||
					throw {
 | 
					 | 
				
			||||||
						message: t.get('application.dns_not_set_error', { domain: domain })
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const publicPort = await getPort({ port: exposePort });
 | 
				
			||||||
 | 
								if (publicPort !== exposePort) {
 | 
				
			||||||
 | 
									throw { message: `Port ${exposePort} is already in use.` };
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (isDNSCheckEnabled && !dev && !forceSave) {
 | 
				
			||||||
 | 
								return await checkDomainsIsValidInDNS({ event, fqdn, dualCerts });
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,10 +19,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
 | 
						import { gitTokens } from '$lib/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let application;
 | 
						export let application;
 | 
				
			||||||
	export let appId;
 | 
						export let appId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$gitTokens.githubToken = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import GithubRepositories from './_GithubRepositories.svelte';
 | 
						import GithubRepositories from './_GithubRepositories.svelte';
 | 
				
			||||||
	import GitlabRepositories from './_GitlabRepositories.svelte';
 | 
						import GitlabRepositories from './_GitlabRepositories.svelte';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
					JSON.stringify({
 | 
										JSON.stringify({
 | 
				
			||||||
						buildPack: applicationFound.buildPack,
 | 
											buildPack: applicationFound.buildPack,
 | 
				
			||||||
						port: applicationFound.port,
 | 
											port: applicationFound.port,
 | 
				
			||||||
 | 
											exposePort: applicationFound.exposePort,
 | 
				
			||||||
						installCommand: applicationFound.installCommand,
 | 
											installCommand: applicationFound.installCommand,
 | 
				
			||||||
						buildCommand: applicationFound.buildCommand,
 | 
											buildCommand: applicationFound.buildCommand,
 | 
				
			||||||
						startCommand: applicationFound.startCommand
 | 
											startCommand: applicationFound.startCommand
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,8 @@
 | 
				
			|||||||
import { getUserDetails } from '$lib/common';
 | 
					import { asyncExecShell, getUserDetails } from '$lib/common';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import { checkContainer, isContainerExited } from '$lib/haproxy';
 | 
					import { checkContainer, getContainerUsage, isContainerExited } from '$lib/haproxy';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
import jsonwebtoken from 'jsonwebtoken';
 | 
					 | 
				
			||||||
import { get as getRequest } from '$lib/api';
 | 
					 | 
				
			||||||
import { setDefaultConfiguration } from '$lib/buildPacks/common';
 | 
					import { setDefaultConfiguration } from '$lib/buildPacks/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get: RequestHandler = async (event) => {
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
@@ -14,21 +12,14 @@ export const get: RequestHandler = async (event) => {
 | 
				
			|||||||
	const { id } = event.params;
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const appId = process.env['COOLIFY_APP_ID'];
 | 
						const appId = process.env['COOLIFY_APP_ID'];
 | 
				
			||||||
	let isRunning = false;
 | 
					 | 
				
			||||||
	let isExited = false;
 | 
					 | 
				
			||||||
	let githubToken = event.locals.cookies?.githubToken || null;
 | 
						let githubToken = event.locals.cookies?.githubToken || null;
 | 
				
			||||||
	let gitlabToken = event.locals.cookies?.gitlabToken || null;
 | 
						let gitlabToken = event.locals.cookies?.gitlabToken || null;
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const application = await db.getApplication({ id, teamId });
 | 
							const application = await db.getApplication({ id, teamId });
 | 
				
			||||||
		if (application.destinationDockerId) {
 | 
					
 | 
				
			||||||
			isRunning = await checkContainer(application.destinationDocker.engine, id);
 | 
					 | 
				
			||||||
			isExited = await isContainerExited(application.destinationDocker.engine, id);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200,
 | 
								status: 200,
 | 
				
			||||||
			body: {
 | 
								body: {
 | 
				
			||||||
				isRunning,
 | 
					 | 
				
			||||||
				isExited,
 | 
					 | 
				
			||||||
				application,
 | 
									application,
 | 
				
			||||||
				appId,
 | 
									appId,
 | 
				
			||||||
				githubToken,
 | 
									githubToken,
 | 
				
			||||||
@@ -52,6 +43,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
		buildPack,
 | 
							buildPack,
 | 
				
			||||||
		fqdn,
 | 
							fqdn,
 | 
				
			||||||
		port,
 | 
							port,
 | 
				
			||||||
 | 
							exposePort,
 | 
				
			||||||
		installCommand,
 | 
							installCommand,
 | 
				
			||||||
		buildCommand,
 | 
							buildCommand,
 | 
				
			||||||
		startCommand,
 | 
							startCommand,
 | 
				
			||||||
@@ -67,6 +59,9 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
		baseBuildImage
 | 
							baseBuildImage
 | 
				
			||||||
	} = await event.request.json();
 | 
						} = await event.request.json();
 | 
				
			||||||
	if (port) port = Number(port);
 | 
						if (port) port = Number(port);
 | 
				
			||||||
 | 
						if (exposePort) {
 | 
				
			||||||
 | 
							exposePort = Number(exposePort);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (denoOptions) denoOptions = denoOptions.trim();
 | 
						if (denoOptions) denoOptions = denoOptions.trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
@@ -87,6 +82,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
			name,
 | 
								name,
 | 
				
			||||||
			fqdn,
 | 
								fqdn,
 | 
				
			||||||
			port,
 | 
								port,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
			installCommand,
 | 
								installCommand,
 | 
				
			||||||
			buildCommand,
 | 
								buildCommand,
 | 
				
			||||||
			startCommand,
 | 
								startCommand,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,7 @@
 | 
				
			|||||||
		if (stuff?.application?.id) {
 | 
							if (stuff?.application?.id) {
 | 
				
			||||||
			return {
 | 
								return {
 | 
				
			||||||
				props: {
 | 
									props: {
 | 
				
			||||||
					application: stuff.application,
 | 
										application: stuff.application
 | 
				
			||||||
					isRunning: stuff.isRunning
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -36,33 +35,46 @@
 | 
				
			|||||||
		baseImages: Array<{ value: string; label: string }>;
 | 
							baseImages: Array<{ value: string; label: string }>;
 | 
				
			||||||
		baseBuildImages: Array<{ value: string; label: string }>;
 | 
							baseBuildImages: Array<{ value: string; label: string }>;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	export let isRunning;
 | 
					 | 
				
			||||||
	import { page, session } from '$app/stores';
 | 
						import { page, session } from '$app/stores';
 | 
				
			||||||
	import { errorNotification } from '$lib/form';
 | 
						import { errorNotification } from '$lib/form';
 | 
				
			||||||
	import { onMount } from 'svelte';
 | 
						import { onDestroy, onMount } from 'svelte';
 | 
				
			||||||
	import Select from 'svelte-select';
 | 
						import Select from 'svelte-select';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import Explainer from '$lib/components/Explainer.svelte';
 | 
						import Explainer from '$lib/components/Explainer.svelte';
 | 
				
			||||||
	import Setting from '$lib/components/Setting.svelte';
 | 
						import Setting from '$lib/components/Setting.svelte';
 | 
				
			||||||
	import type Prisma from '@prisma/client';
 | 
						import type Prisma from '@prisma/client';
 | 
				
			||||||
	import { notNodeDeployments, staticDeployments } from '$lib/components/common';
 | 
						import { getDomain, notNodeDeployments, staticDeployments } from '$lib/components/common';
 | 
				
			||||||
	import { toast } from '@zerodevx/svelte-toast';
 | 
						import { toast } from '@zerodevx/svelte-toast';
 | 
				
			||||||
	import { post } from '$lib/api';
 | 
						import { get, post } from '$lib/api';
 | 
				
			||||||
	import cuid from 'cuid';
 | 
						import cuid from 'cuid';
 | 
				
			||||||
	import { browser } from '$app/env';
 | 
						import { browser } from '$app/env';
 | 
				
			||||||
	import { disabledButton } from '$lib/store';
 | 
						import { disabledButton, status } from '$lib/store';
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
	const { id } = $page.params;
 | 
						const { id } = $page.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let domainEl: HTMLInputElement;
 | 
						let domainEl: HTMLInputElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let loading = false;
 | 
						let loading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let usageLoading = false;
 | 
				
			||||||
 | 
						let usage = {
 | 
				
			||||||
 | 
							MemUsage: 0,
 | 
				
			||||||
 | 
							CPUPerc: 0,
 | 
				
			||||||
 | 
							NetIO: 0
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						let usageInterval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let forceSave = false;
 | 
						let forceSave = false;
 | 
				
			||||||
	let debug = application.settings.debug;
 | 
						let debug = application.settings.debug;
 | 
				
			||||||
	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 nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
 | 
				
			||||||
 | 
						let isNonWWWDomainOK = false;
 | 
				
			||||||
 | 
						let isWWWDomainOK = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$: isDisabled = !$session.isAdmin || $status.application.isRunning;
 | 
				
			||||||
	let wsgis = [
 | 
						let wsgis = [
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			value: 'None',
 | 
								value: 'None',
 | 
				
			||||||
@@ -74,15 +86,29 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	];
 | 
						];
 | 
				
			||||||
	function containerClass() {
 | 
						function containerClass() {
 | 
				
			||||||
		if (!$session.isAdmin || isRunning) {
 | 
							return 'text-white border border-dashed border-coolgray-300 bg-transparent font-thin px-0';
 | 
				
			||||||
			return 'text-white border border-dashed border-coolgray-300 bg-transparent font-thin px-0';
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async function getUsage() {
 | 
				
			||||||
 | 
							if (usageLoading) return;
 | 
				
			||||||
 | 
							usageLoading = true;
 | 
				
			||||||
 | 
							const data = await get(`/applications/${id}/usage.json`);
 | 
				
			||||||
 | 
							usage = data.usage;
 | 
				
			||||||
 | 
							usageLoading = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						onDestroy(() => {
 | 
				
			||||||
 | 
							clearInterval(usageInterval);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						onMount(async () => {
 | 
				
			||||||
 | 
							if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
 | 
				
			||||||
 | 
								application.fqdn = `http://${cuid()}.demo.coolify.io`;
 | 
				
			||||||
 | 
								await post(`/applications/${id}.json`, { ...application });
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
 | 
					 | 
				
			||||||
		application.fqdn = `http://${cuid()}.demo.coolify.io`;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	onMount(() => {
 | 
					 | 
				
			||||||
		domainEl.focus();
 | 
							domainEl.focus();
 | 
				
			||||||
 | 
							await getUsage();
 | 
				
			||||||
 | 
							usageInterval = setInterval(async () => {
 | 
				
			||||||
 | 
								await getUsage();
 | 
				
			||||||
 | 
							}, 1000);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function changeSettings(name) {
 | 
						async function changeSettings(name) {
 | 
				
			||||||
@@ -125,15 +151,34 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	async function handleSubmit() {
 | 
						async function handleSubmit() {
 | 
				
			||||||
 | 
							if (loading) return;
 | 
				
			||||||
		loading = true;
 | 
							loading = true;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave });
 | 
								nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
 | 
				
			||||||
 | 
								await post(`/applications/${id}/check.json`, {
 | 
				
			||||||
 | 
									fqdn: application.fqdn,
 | 
				
			||||||
 | 
									forceSave,
 | 
				
			||||||
 | 
									dualCerts,
 | 
				
			||||||
 | 
									exposePort: application.exposePort
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
			await post(`/applications/${id}.json`, { ...application });
 | 
								await post(`/applications/${id}.json`, { ...application });
 | 
				
			||||||
			$disabledButton = false;
 | 
								$disabledButton = false;
 | 
				
			||||||
 | 
								forceSave = false;
 | 
				
			||||||
			return toast.push('Configurations saved.');
 | 
								return toast.push('Configurations saved.');
 | 
				
			||||||
		} catch ({ error }) {
 | 
							} catch ({ error }) {
 | 
				
			||||||
			if (error?.startsWith($t('application.dns_not_set_partial_error'))) {
 | 
								if (error?.startsWith($t('application.dns_not_set_partial_error'))) {
 | 
				
			||||||
				forceSave = true;
 | 
									forceSave = true;
 | 
				
			||||||
 | 
									if (dualCerts) {
 | 
				
			||||||
 | 
										isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
 | 
				
			||||||
 | 
										isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										const isWWW = getDomain(application.fqdn).includes('www.');
 | 
				
			||||||
 | 
										if (isWWW) {
 | 
				
			||||||
 | 
											isWWWDomainOK = await isDNSValid(getDomain(`www.${nonWWWDomain}`), true);
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											isNonWWWDomainOK = await isDNSValid(getDomain(nonWWWDomain), false);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return errorNotification(error);
 | 
								return errorNotification(error);
 | 
				
			||||||
		} finally {
 | 
							} finally {
 | 
				
			||||||
@@ -151,6 +196,19 @@
 | 
				
			|||||||
		application.baseBuildImage = event.detail.value;
 | 
							application.baseBuildImage = event.detail.value;
 | 
				
			||||||
		await handleSubmit();
 | 
							await handleSubmit();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async function isDNSValid(domain, isWWW) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await get(`/applications/${id}/check.json?domain=${domain}`);
 | 
				
			||||||
 | 
								toast.push('DNS configuration is valid.');
 | 
				
			||||||
 | 
								isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							} catch ({ error }) {
 | 
				
			||||||
 | 
								errorNotification(error);
 | 
				
			||||||
 | 
								isWWW ? (isWWWDomainOK = false) : (isNonWWWDomainOK = false);
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex items-center space-x-2 p-5 px-6 font-bold">
 | 
					<div class="flex items-center space-x-2 p-5 px-6 font-bold">
 | 
				
			||||||
@@ -226,6 +284,33 @@
 | 
				
			|||||||
	</a>
 | 
						</a>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="mx-auto max-w-4xl px-6 py-4">
 | 
				
			||||||
 | 
						<div class="text-2xl font-bold">Application Usage</div>
 | 
				
			||||||
 | 
						<div class="mx-auto">
 | 
				
			||||||
 | 
							<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class=" text-sm font-medium text-white">Used Memory / Memory Limit</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white">
 | 
				
			||||||
 | 
										{usage?.MemUsage}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">Used CPU</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white ">
 | 
				
			||||||
 | 
										{usage?.CPUPerc}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">Network IO</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white ">
 | 
				
			||||||
 | 
										{usage?.NetIO}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</dl>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
<div class="mx-auto max-w-4xl px-6">
 | 
					<div class="mx-auto max-w-4xl px-6">
 | 
				
			||||||
	<!-- svelte-ignore missing-declaration -->
 | 
						<!-- svelte-ignore missing-declaration -->
 | 
				
			||||||
	<form on:submit|preventDefault={handleSubmit} class="py-4">
 | 
						<form on:submit|preventDefault={handleSubmit} class="py-4">
 | 
				
			||||||
@@ -319,28 +404,30 @@
 | 
				
			|||||||
						value={application.destinationDocker.name}
 | 
											value={application.destinationDocker.name}
 | 
				
			||||||
						id="destination"
 | 
											id="destination"
 | 
				
			||||||
						disabled
 | 
											disabled
 | 
				
			||||||
						class="bg-transparent "
 | 
											class="bg-transparent"
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="grid grid-cols-2 items-center">
 | 
								{#if application.buildPack !== 'docker'}
 | 
				
			||||||
				<label for="baseImage" class="text-base font-bold text-stone-100"
 | 
									<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
					>{$t('application.base_image')}</label
 | 
										<label for="baseImage" class="text-base font-bold text-stone-100"
 | 
				
			||||||
				>
 | 
											>{$t('application.base_image')}</label
 | 
				
			||||||
				<div class="custom-select-wrapper">
 | 
										>
 | 
				
			||||||
					<Select
 | 
										<div class="custom-select-wrapper">
 | 
				
			||||||
						isDisabled={!$session.isAdmin || isRunning}
 | 
											<Select
 | 
				
			||||||
						containerClasses={containerClass()}
 | 
												{isDisabled}
 | 
				
			||||||
						id="baseImages"
 | 
												containerClasses={isDisabled && containerClass()}
 | 
				
			||||||
						showIndicator={!isRunning}
 | 
												id="baseImages"
 | 
				
			||||||
						items={application.baseImages}
 | 
												showIndicator={!$status.application.isRunning}
 | 
				
			||||||
						on:select={selectBaseImage}
 | 
												items={application.baseImages}
 | 
				
			||||||
						value={application.baseImage}
 | 
												on:select={selectBaseImage}
 | 
				
			||||||
						isClearable={false}
 | 
												value={application.baseImage}
 | 
				
			||||||
					/>
 | 
												isClearable={false}
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<Explainer text={$t('application.base_image_explainer')} />
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<Explainer text={$t('application.base_image_explainer')} />
 | 
								{/if}
 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
 | 
								{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
 | 
				
			||||||
				<div class="grid grid-cols-2 items-center pb-8">
 | 
									<div class="grid grid-cols-2 items-center pb-8">
 | 
				
			||||||
					<label for="baseBuildImage" class="text-base font-bold text-stone-100"
 | 
										<label for="baseBuildImage" class="text-base font-bold text-stone-100"
 | 
				
			||||||
@@ -349,10 +436,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					<div class="custom-select-wrapper">
 | 
										<div class="custom-select-wrapper">
 | 
				
			||||||
						<Select
 | 
											<Select
 | 
				
			||||||
							isDisabled={!$session.isAdmin || isRunning}
 | 
												{isDisabled}
 | 
				
			||||||
							containerClasses={containerClass()}
 | 
												containerClasses={isDisabled && containerClass()}
 | 
				
			||||||
							id="baseBuildImages"
 | 
												id="baseBuildImages"
 | 
				
			||||||
							showIndicator={!isRunning}
 | 
												showIndicator={!$status.application.isRunning}
 | 
				
			||||||
							items={application.baseBuildImages}
 | 
												items={application.baseBuildImages}
 | 
				
			||||||
							on:select={selectBaseBuildImage}
 | 
												on:select={selectBaseBuildImage}
 | 
				
			||||||
							value={application.baseBuildImage}
 | 
												value={application.baseBuildImage}
 | 
				
			||||||
@@ -383,27 +470,62 @@
 | 
				
			|||||||
					{/if}
 | 
										{/if}
 | 
				
			||||||
					<Explainer text={$t('application.https_explainer')} />
 | 
										<Explainer text={$t('application.https_explainer')} />
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<input
 | 
									<div>
 | 
				
			||||||
					readonly={!$session.isAdmin || isRunning}
 | 
										<input
 | 
				
			||||||
					disabled={!$session.isAdmin || isRunning}
 | 
											readonly={isDisabled}
 | 
				
			||||||
					bind:this={domainEl}
 | 
											disabled={isDisabled}
 | 
				
			||||||
					name="fqdn"
 | 
											bind:this={domainEl}
 | 
				
			||||||
					id="fqdn"
 | 
											name="fqdn"
 | 
				
			||||||
					bind:value={application.fqdn}
 | 
											id="fqdn"
 | 
				
			||||||
					pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
											bind:value={application.fqdn}
 | 
				
			||||||
					placeholder="eg: https://coollabs.io"
 | 
											pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
				
			||||||
					required
 | 
											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
 | 
				
			||||||
 | 
															class="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="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 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={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={() => !isRunning && changeSettings('dualCerts')}
 | 
										on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			{#if application.buildPack === 'python'}
 | 
								{#if application.buildPack === 'python'}
 | 
				
			||||||
@@ -451,9 +573,24 @@
 | 
				
			|||||||
					/>
 | 
										/>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			{/if}
 | 
								{/if}
 | 
				
			||||||
 | 
								{#if application.buildPack !== 'docker'}
 | 
				
			||||||
			{#if !notNodeDeployments.includes(application.buildPack)}
 | 
					 | 
				
			||||||
				<div class="grid grid-cols-2 items-center">
 | 
									<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
										<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
 | 
				
			||||||
 | 
										<input
 | 
				
			||||||
 | 
											readonly={!$session.isAdmin && !$status.application.isRunning}
 | 
				
			||||||
 | 
											disabled={isDisabled}
 | 
				
			||||||
 | 
											name="exposePort"
 | 
				
			||||||
 | 
											id="exposePort"
 | 
				
			||||||
 | 
											bind:value={application.exposePort}
 | 
				
			||||||
 | 
											placeholder="12345"
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
										<Explainer
 | 
				
			||||||
 | 
											text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
								{#if !notNodeDeployments.includes(application.buildPack)}
 | 
				
			||||||
 | 
									<div class="grid grid-cols-2 items-center pt-4">
 | 
				
			||||||
					<label for="installCommand" class="text-base font-bold text-stone-100"
 | 
										<label for="installCommand" class="text-base font-bold text-stone-100"
 | 
				
			||||||
						>{$t('application.install_command')}</label
 | 
											>{$t('application.install_command')}</label
 | 
				
			||||||
					>
 | 
										>
 | 
				
			||||||
@@ -491,7 +628,7 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			{/if}
 | 
								{/if}
 | 
				
			||||||
			{#if application.buildPack === 'docker'}
 | 
								{#if application.buildPack === 'docker'}
 | 
				
			||||||
				<div class="grid grid-cols-2 items-center">
 | 
									<div class="grid grid-cols-2 items-center pt-4">
 | 
				
			||||||
					<label for="dockerFileLocation" class="text-base font-bold text-stone-100"
 | 
										<label for="dockerFileLocation" class="text-base font-bold text-stone-100"
 | 
				
			||||||
						>Dockerfile Location</label
 | 
											>Dockerfile Location</label
 | 
				
			||||||
					>
 | 
										>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,8 @@
 | 
				
			|||||||
<div class="lds-ripple absolute left-0">
 | 
					<script>
 | 
				
			||||||
 | 
						export let position = 'absolute';
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="lds-ripple {position} left-0">
 | 
				
			||||||
	<div />
 | 
						<div />
 | 
				
			||||||
	<div />
 | 
						<div />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,23 +129,28 @@
 | 
				
			|||||||
				{#if currentStatus === 'running'}
 | 
									{#if currentStatus === 'running'}
 | 
				
			||||||
					<button
 | 
										<button
 | 
				
			||||||
						on:click={cancelBuild}
 | 
											on:click={cancelBuild}
 | 
				
			||||||
 | 
											class:animation-spin={cancelInprogress}
 | 
				
			||||||
						class="bg-transparent hover:text-red-500 hover:bg-coolgray-500"
 | 
											class="bg-transparent hover:text-red-500 hover:bg-coolgray-500"
 | 
				
			||||||
						data-tooltip="Cancel build"
 | 
											data-tooltip="Cancel build"
 | 
				
			||||||
					>
 | 
										>
 | 
				
			||||||
						<svg
 | 
											{#if cancelInprogress}
 | 
				
			||||||
							xmlns="http://www.w3.org/2000/svg"
 | 
												Cancelling...
 | 
				
			||||||
							class="w-6 h-6"
 | 
											{:else}
 | 
				
			||||||
							viewBox="0 0 24 24"
 | 
												<svg
 | 
				
			||||||
							stroke-width="1.5"
 | 
													xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
							stroke="currentColor"
 | 
													class="w-6 h-6"
 | 
				
			||||||
							fill="none"
 | 
													viewBox="0 0 24 24"
 | 
				
			||||||
							stroke-linecap="round"
 | 
													stroke-width="1.5"
 | 
				
			||||||
							stroke-linejoin="round"
 | 
													stroke="currentColor"
 | 
				
			||||||
						>
 | 
													fill="none"
 | 
				
			||||||
							<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
													stroke-linecap="round"
 | 
				
			||||||
							<circle cx="12" cy="12" r="9" />
 | 
													stroke-linejoin="round"
 | 
				
			||||||
							<path d="M10 10l4 4m0 -4l-4 4" />
 | 
												>
 | 
				
			||||||
						</svg>
 | 
													<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
													<circle cx="12" cy="12" r="9" />
 | 
				
			||||||
 | 
													<path d="M10 10l4 4m0 -4l-4 4" />
 | 
				
			||||||
 | 
												</svg>
 | 
				
			||||||
 | 
											{/if}
 | 
				
			||||||
					</button>
 | 
										</button>
 | 
				
			||||||
				{/if}
 | 
									{/if}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ export const get: RequestHandler = async (event) => {
 | 
				
			|||||||
		const destinationDocker = await db.getDestinationByApplicationId({ id, teamId });
 | 
							const destinationDocker = await db.getDestinationByApplicationId({ id, teamId });
 | 
				
			||||||
		const docker = dockerInstance({ destinationDocker });
 | 
							const docker = dockerInstance({ destinationDocker });
 | 
				
			||||||
		const listContainers = await docker.engine.listContainers({
 | 
							const listContainers = await docker.engine.listContainers({
 | 
				
			||||||
			filters: { network: [destinationDocker.network] }
 | 
								filters: { network: [destinationDocker.network], name: [id] }
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		const containers = listContainers.filter((container) => {
 | 
							const containers = listContainers.filter((container) => {
 | 
				
			||||||
			return (
 | 
								return (
 | 
				
			||||||
@@ -30,11 +30,7 @@ export const get: RequestHandler = async (event) => {
 | 
				
			|||||||
				JSON.parse(Buffer.from(container.Labels['coolify.configuration'], 'base64').toString())
 | 
									JSON.parse(Buffer.from(container.Labels['coolify.configuration'], 'base64').toString())
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
			.filter((container) => {
 | 
								.filter((container) => {
 | 
				
			||||||
				return (
 | 
									return container.pullmergeRequestId && container.applicationId === id;
 | 
				
			||||||
					container.type !== 'manual' &&
 | 
					 | 
				
			||||||
					container.type !== 'webhook_commit' &&
 | 
					 | 
				
			||||||
					container.applicationId === id
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			body: {
 | 
								body: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@
 | 
				
			|||||||
	import { errorNotification } from '$lib/form';
 | 
						import { errorNotification } from '$lib/form';
 | 
				
			||||||
	import { toast } from '@zerodevx/svelte-toast';
 | 
						import { toast } from '@zerodevx/svelte-toast';
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
 | 
						import { goto } from '$app/navigation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { id } = $page.params;
 | 
						const { id } = $page.params;
 | 
				
			||||||
	async function refreshSecrets() {
 | 
						async function refreshSecrets() {
 | 
				
			||||||
@@ -39,11 +40,18 @@
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	async function redeploy(container) {
 | 
						async function redeploy(container) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			await post(`/applications/${id}/deploy.json`, {
 | 
								const { buildId } = await post(`/applications/${id}/deploy.json`, {
 | 
				
			||||||
				pullmergeRequestId: container.pullmergeRequestId,
 | 
									pullmergeRequestId: container.pullmergeRequestId,
 | 
				
			||||||
				branch: container.branch
 | 
									branch: container.branch
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			toast.push('Application redeployed queued.');
 | 
								toast.push('Application redeployed queued.');
 | 
				
			||||||
 | 
								if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
 | 
				
			||||||
 | 
									return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return await goto(`/applications/${id}/logs/build?buildId=${buildId}`, {
 | 
				
			||||||
 | 
										replaceState: true
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} catch ({ error }) {
 | 
							} catch ({ error }) {
 | 
				
			||||||
			return errorNotification(error);
 | 
								return errorNotification(error);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								src/routes/applications/[id]/status.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/routes/applications/[id]/status.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { asyncExecShell, getUserDetails } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import { checkContainer, getContainerUsage, isContainerExited } from '$lib/haproxy';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import { setDefaultConfiguration } from '$lib/buildPacks/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let isRunning = false;
 | 
				
			||||||
 | 
						let isExited = false;
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const application = await db.getApplication({ id, teamId });
 | 
				
			||||||
 | 
							if (application.destinationDockerId) {
 | 
				
			||||||
 | 
								[isRunning, isExited] = await Promise.all([
 | 
				
			||||||
 | 
									checkContainer(application.destinationDocker.engine, id),
 | 
				
			||||||
 | 
									isContainerExited(application.destinationDocker.engine, id)
 | 
				
			||||||
 | 
								]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 200,
 | 
				
			||||||
 | 
								body: {
 | 
				
			||||||
 | 
									isRunning,
 | 
				
			||||||
 | 
									isExited
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								headers: {}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(error);
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/routes/applications/[id]/usage.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/routes/applications/[id]/usage.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import { getContainerUsage } from '$lib/haproxy';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let usage = {};
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const application = await db.getApplication({ id, teamId });
 | 
				
			||||||
 | 
							if (application.destinationDockerId) {
 | 
				
			||||||
 | 
								[usage] = await Promise.all([getContainerUsage(application.destinationDocker.engine, id)]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 200,
 | 
				
			||||||
 | 
								body: {
 | 
				
			||||||
 | 
									usage
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								headers: {}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(error);
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -60,7 +60,7 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="flex flex-col flex-wrap justify-center">
 | 
					<div class="flex-col justify-center">
 | 
				
			||||||
	{#if !applications || ownApplications.length === 0}
 | 
						{#if !applications || ownApplications.length === 0}
 | 
				
			||||||
		<div class="flex-col">
 | 
							<div class="flex-col">
 | 
				
			||||||
			<div class="text-center text-xl font-bold">{$t('application.no_applications_found')}</div>
 | 
								<div class="text-center text-xl font-bold">{$t('application.no_applications_found')}</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,45 +2,69 @@ import { getUserDetails } from '$lib/common';
 | 
				
			|||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import os from 'node:os';
 | 
				
			||||||
 | 
					import osu from 'node-os-utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get: RequestHandler = async (event) => {
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
	const { userId, teamId, status, body } = await getUserDetails(event);
 | 
						const { userId, teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
	if (status === 401) return { status, body };
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
						const usage = event.url.searchParams.get('usage');
 | 
				
			||||||
	try {
 | 
						if (usage) {
 | 
				
			||||||
		const applicationsCount = await db.prisma.application.count({
 | 
							try {
 | 
				
			||||||
			where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
								return {
 | 
				
			||||||
		});
 | 
									status: 200,
 | 
				
			||||||
		const sourcesCount = await db.prisma.gitSource.count({
 | 
									body: {
 | 
				
			||||||
			where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
										uptime: os.uptime(),
 | 
				
			||||||
		});
 | 
										memory: await osu.mem.info(),
 | 
				
			||||||
		const destinationsCount = await db.prisma.destinationDocker.count({
 | 
										cpu: {
 | 
				
			||||||
			where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
											load: os.loadavg(),
 | 
				
			||||||
		});
 | 
											usage: await osu.cpu.usage(),
 | 
				
			||||||
		const teamsCount = await db.prisma.permission.count({ where: { userId } });
 | 
											count: os.cpus().length
 | 
				
			||||||
		const databasesCount = await db.prisma.database.count({
 | 
										},
 | 
				
			||||||
			where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
										disk: await osu.drive.info()
 | 
				
			||||||
		});
 | 
									}
 | 
				
			||||||
		const servicesCount = await db.prisma.service.count({
 | 
								};
 | 
				
			||||||
			where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
							} catch (error) {
 | 
				
			||||||
		});
 | 
								return ErrorHandler(error);
 | 
				
			||||||
		const teams = await db.prisma.permission.findMany({
 | 
							}
 | 
				
			||||||
			where: { userId },
 | 
						} else {
 | 
				
			||||||
			include: { team: { include: { _count: { select: { users: true } } } } }
 | 
							try {
 | 
				
			||||||
		});
 | 
								const applicationsCount = await db.prisma.application.count({
 | 
				
			||||||
		return {
 | 
									where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
				
			||||||
			body: {
 | 
								});
 | 
				
			||||||
				teams,
 | 
								const sourcesCount = await db.prisma.gitSource.count({
 | 
				
			||||||
				applicationsCount,
 | 
									where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
				
			||||||
				sourcesCount,
 | 
								});
 | 
				
			||||||
				destinationsCount,
 | 
								const destinationsCount = await db.prisma.destinationDocker.count({
 | 
				
			||||||
				teamsCount,
 | 
									where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
				
			||||||
				databasesCount,
 | 
								});
 | 
				
			||||||
				servicesCount
 | 
								const teamsCount = await db.prisma.permission.count({ where: { userId } });
 | 
				
			||||||
			}
 | 
								const databasesCount = await db.prisma.database.count({
 | 
				
			||||||
		};
 | 
									where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
				
			||||||
	} catch (error) {
 | 
								});
 | 
				
			||||||
		return ErrorHandler(error);
 | 
								const servicesCount = await db.prisma.service.count({
 | 
				
			||||||
 | 
									where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								const teams = await db.prisma.permission.findMany({
 | 
				
			||||||
 | 
									where: { userId },
 | 
				
			||||||
 | 
									include: { team: { include: { _count: { select: { users: true } } } } }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								const settings = await db.prisma.setting.findFirst();
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									body: {
 | 
				
			||||||
 | 
										teams,
 | 
				
			||||||
 | 
										applicationsCount,
 | 
				
			||||||
 | 
										sourcesCount,
 | 
				
			||||||
 | 
										destinationsCount,
 | 
				
			||||||
 | 
										teamsCount,
 | 
				
			||||||
 | 
										databasesCount,
 | 
				
			||||||
 | 
										servicesCount,
 | 
				
			||||||
 | 
										settings
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								return ErrorHandler(error);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	import MySql from './_MySQL.svelte';
 | 
						import MySql from './_MySQL.svelte';
 | 
				
			||||||
	import MongoDb from './_MongoDB.svelte';
 | 
						import MongoDb from './_MongoDB.svelte';
 | 
				
			||||||
 | 
						import MariaDb from './_MariaDB.svelte';
 | 
				
			||||||
	import PostgreSql from './_PostgreSQL.svelte';
 | 
						import PostgreSql from './_PostgreSQL.svelte';
 | 
				
			||||||
	import Redis from './_Redis.svelte';
 | 
						import Redis from './_Redis.svelte';
 | 
				
			||||||
	import CouchDb from './_CouchDb.svelte';
 | 
						import CouchDb from './_CouchDb.svelte';
 | 
				
			||||||
@@ -190,6 +191,8 @@
 | 
				
			|||||||
				<PostgreSql bind:database {isRunning} />
 | 
									<PostgreSql bind:database {isRunning} />
 | 
				
			||||||
			{:else if database.type === 'mongodb'}
 | 
								{:else if database.type === 'mongodb'}
 | 
				
			||||||
				<MongoDb bind:database {isRunning} />
 | 
									<MongoDb bind:database {isRunning} />
 | 
				
			||||||
 | 
								{:else if database.type === 'mariadb'}
 | 
				
			||||||
 | 
									<MariaDb bind:database {isRunning} />
 | 
				
			||||||
			{:else if database.type === 'redis'}
 | 
								{:else if database.type === 'redis'}
 | 
				
			||||||
				<Redis bind:database {isRunning} />
 | 
									<Redis bind:database {isRunning} />
 | 
				
			||||||
			{:else if database.type === 'couchdb'}
 | 
								{:else if database.type === 'couchdb'}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										79
									
								
								src/routes/databases/[id]/_Databases/_MariaDB.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/routes/databases/[id]/_Databases/_MariaDB.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<script>
 | 
				
			||||||
 | 
						export let database;
 | 
				
			||||||
 | 
						export let isRunning;
 | 
				
			||||||
 | 
						import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
 | 
				
			||||||
 | 
						import Explainer from '$lib/components/Explainer.svelte';
 | 
				
			||||||
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="flex space-x-1 py-5 font-bold">
 | 
				
			||||||
 | 
						<div class="title">MariaDB</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="space-y-2 px-10">
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
							<label for="defaultDatabase" class="text-base font-bold text-stone-100"
 | 
				
			||||||
 | 
								>{$t('database.default_database')}</label
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
							<CopyPasswordField
 | 
				
			||||||
 | 
								required
 | 
				
			||||||
 | 
								readonly={database.defaultDatabase}
 | 
				
			||||||
 | 
								disabled={database.defaultDatabase}
 | 
				
			||||||
 | 
								placeholder="{$t('forms.eg')}: mydb"
 | 
				
			||||||
 | 
								id="defaultDatabase"
 | 
				
			||||||
 | 
								name="defaultDatabase"
 | 
				
			||||||
 | 
								bind:value={database.defaultDatabase}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
							<label for="dbUser" class="text-base font-bold text-stone-100">{$t('forms.user')}</label>
 | 
				
			||||||
 | 
							<CopyPasswordField
 | 
				
			||||||
 | 
								readonly
 | 
				
			||||||
 | 
								disabled
 | 
				
			||||||
 | 
								placeholder={$t('forms.generated_automatically_after_start')}
 | 
				
			||||||
 | 
								id="dbUser"
 | 
				
			||||||
 | 
								name="dbUser"
 | 
				
			||||||
 | 
								value={database.dbUser}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
							<label for="dbUserPassword" class="text-base font-bold text-stone-100"
 | 
				
			||||||
 | 
								>{$t('forms.password')}</label
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
							<CopyPasswordField
 | 
				
			||||||
 | 
								disabled={!isRunning}
 | 
				
			||||||
 | 
								readonly={!isRunning}
 | 
				
			||||||
 | 
								placeholder={$t('forms.generated_automatically_after_start')}
 | 
				
			||||||
 | 
								isPasswordField
 | 
				
			||||||
 | 
								id="dbUserPassword"
 | 
				
			||||||
 | 
								name="dbUserPassword"
 | 
				
			||||||
 | 
								bind:value={database.dbUserPassword}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
							<Explainer text="Could be changed while the database is running." />
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
							<label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label>
 | 
				
			||||||
 | 
							<CopyPasswordField
 | 
				
			||||||
 | 
								readonly
 | 
				
			||||||
 | 
								disabled
 | 
				
			||||||
 | 
								placeholder={$t('forms.generated_automatically_after_start')}
 | 
				
			||||||
 | 
								id="rootUser"
 | 
				
			||||||
 | 
								name="rootUser"
 | 
				
			||||||
 | 
								value={database.rootUser}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center">
 | 
				
			||||||
 | 
							<label for="rootUserPassword" class="text-base font-bold text-stone-100"
 | 
				
			||||||
 | 
								>{$t('forms.roots_password')}</label
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
							<CopyPasswordField
 | 
				
			||||||
 | 
								disabled={!isRunning}
 | 
				
			||||||
 | 
								readonly={!isRunning}
 | 
				
			||||||
 | 
								placeholder={$t('forms.generated_automatically_after_start')}
 | 
				
			||||||
 | 
								isPasswordField
 | 
				
			||||||
 | 
								id="rootUserPassword"
 | 
				
			||||||
 | 
								name="rootUserPassword"
 | 
				
			||||||
 | 
								bind:value={database.rootUserPassword}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
							<Explainer text="Could be changed while the database is running." />
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -37,6 +37,7 @@
 | 
				
			|||||||
	import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
 | 
						import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
 | 
				
			||||||
	import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
 | 
						import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
 | 
				
			||||||
	import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
 | 
						import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
 | 
				
			||||||
 | 
						import MariaDB from '$lib/components/svg/databases/MariaDB.svelte';
 | 
				
			||||||
	import MySQL from '$lib/components/svg/databases/MySQL.svelte';
 | 
						import MySQL from '$lib/components/svg/databases/MySQL.svelte';
 | 
				
			||||||
	import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
 | 
						import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
 | 
				
			||||||
	import Redis from '$lib/components/svg/databases/Redis.svelte';
 | 
						import Redis from '$lib/components/svg/databases/Redis.svelte';
 | 
				
			||||||
@@ -68,6 +69,8 @@
 | 
				
			|||||||
						<CouchDB isAbsolute />
 | 
											<CouchDB isAbsolute />
 | 
				
			||||||
					{:else if type.name === 'mongodb'}
 | 
										{:else if type.name === 'mongodb'}
 | 
				
			||||||
						<MongoDB isAbsolute />
 | 
											<MongoDB isAbsolute />
 | 
				
			||||||
 | 
										{:else if type.name === 'mariadb'}
 | 
				
			||||||
 | 
											<MariaDB isAbsolute />
 | 
				
			||||||
					{:else if type.name === 'mysql'}
 | 
										{:else if type.name === 'mysql'}
 | 
				
			||||||
						<MySQL isAbsolute />
 | 
											<MySQL isAbsolute />
 | 
				
			||||||
					{:else if type.name === 'postgresql'}
 | 
										{:else if type.name === 'postgresql'}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,10 +33,40 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import DatabaseLinks from '$lib/components/DatabaseLinks.svelte';
 | 
						import DatabaseLinks from '$lib/components/DatabaseLinks.svelte';
 | 
				
			||||||
 | 
						import { page } from '$app/stores';
 | 
				
			||||||
 | 
						import { get } from '$lib/api';
 | 
				
			||||||
 | 
						import { onDestroy, onMount } from 'svelte';
 | 
				
			||||||
	export let database;
 | 
						export let database;
 | 
				
			||||||
	export let settings;
 | 
						export let settings;
 | 
				
			||||||
	export let privatePort;
 | 
						export let privatePort;
 | 
				
			||||||
	export let isRunning;
 | 
						export let isRunning;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = $page.params;
 | 
				
			||||||
 | 
						let usageLoading = false;
 | 
				
			||||||
 | 
						let usage = {
 | 
				
			||||||
 | 
							MemUsage: 0,
 | 
				
			||||||
 | 
							CPUPerc: 0,
 | 
				
			||||||
 | 
							NetIO: 0
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						let usageInterval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async function getUsage() {
 | 
				
			||||||
 | 
							if (usageLoading) return;
 | 
				
			||||||
 | 
							usageLoading = true;
 | 
				
			||||||
 | 
							const data = await get(`/databases/${id}/usage.json`);
 | 
				
			||||||
 | 
							usage = data.usage;
 | 
				
			||||||
 | 
							usageLoading = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						onDestroy(() => {
 | 
				
			||||||
 | 
							clearInterval(usageInterval);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						onMount(async () => {
 | 
				
			||||||
 | 
							await getUsage();
 | 
				
			||||||
 | 
							usageInterval = setInterval(async () => {
 | 
				
			||||||
 | 
								await getUsage();
 | 
				
			||||||
 | 
							}, 1000);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex items-center space-x-2 p-6 text-2xl font-bold">
 | 
					<div class="flex items-center space-x-2 p-6 text-2xl font-bold">
 | 
				
			||||||
@@ -49,4 +79,31 @@
 | 
				
			|||||||
	<DatabaseLinks {database} />
 | 
						<DatabaseLinks {database} />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="mx-auto max-w-4xl px-6 py-4">
 | 
				
			||||||
 | 
						<div class="text-2xl font-bold">Database Usage</div>
 | 
				
			||||||
 | 
						<div class="mx-auto">
 | 
				
			||||||
 | 
							<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class=" text-sm font-medium text-white">Used Memory / Memory Limit</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white">
 | 
				
			||||||
 | 
										{usage?.MemUsage}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">Used CPU</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white ">
 | 
				
			||||||
 | 
										{usage?.CPUPerc}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">Network IO</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-xl font-semibold text-white ">
 | 
				
			||||||
 | 
										{usage?.NetIO}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</dl>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
<Databases bind:database {privatePort} {settings} {isRunning} />
 | 
					<Databases bind:database {privatePort} {settings} {isRunning} />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import { getUserDetails } from '$lib/common';
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { generateDatabaseConfiguration, ErrorHandler, getFreePort } from '$lib/database';
 | 
					import { generateDatabaseConfiguration, ErrorHandler, getFreePort } from '$lib/database';
 | 
				
			||||||
import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
 | 
					import { startTcpProxy, startTraefikTCPProxy, stopTcpHttpProxy } from '$lib/haproxy';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
@@ -13,6 +13,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
	const publicPort = await getFreePort();
 | 
						const publicPort = await getFreePort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
 | 
							const settings = await db.listSettings();
 | 
				
			||||||
		await db.setDatabase({ id, isPublic, appendOnly });
 | 
							await db.setDatabase({ id, isPublic, appendOnly });
 | 
				
			||||||
		const database = await db.getDatabase({ id, teamId });
 | 
							const database = await db.getDatabase({ id, teamId });
 | 
				
			||||||
		const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
 | 
							const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
 | 
				
			||||||
@@ -21,7 +22,11 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
		if (destinationDockerId) {
 | 
							if (destinationDockerId) {
 | 
				
			||||||
			if (isPublic) {
 | 
								if (isPublic) {
 | 
				
			||||||
				await db.prisma.database.update({ where: { id }, data: { publicPort } });
 | 
									await db.prisma.database.update({ where: { id }, data: { publicPort } });
 | 
				
			||||||
				await startTcpProxy(destinationDocker, id, publicPort, privatePort);
 | 
									if (settings.isTraefikUsed) {
 | 
				
			||||||
 | 
										await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										await startTcpProxy(destinationDocker, id, publicPort, privatePort);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
 | 
									await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
 | 
				
			||||||
				await stopTcpHttpProxy(destinationDocker, oldPublicPort);
 | 
									await stopTcpHttpProxy(destinationDocker, oldPublicPort);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								src/routes/databases/[id]/usage.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/routes/databases/[id]/usage.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import { getContainerUsage } from '$lib/haproxy';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const get: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let usage = {};
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const database = await db.getDatabase({ id, teamId });
 | 
				
			||||||
 | 
							if (database.destinationDockerId) {
 | 
				
			||||||
 | 
								[usage] = await Promise.all([getContainerUsage(database.destinationDocker.engine, id)]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 200,
 | 
				
			||||||
 | 
								body: {
 | 
				
			||||||
 | 
									usage
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								headers: {}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log(error);
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
	import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
 | 
						import Clickhouse from '$lib/components/svg/databases/Clickhouse.svelte';
 | 
				
			||||||
	import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
 | 
						import CouchDB from '$lib/components/svg/databases/CouchDB.svelte';
 | 
				
			||||||
	import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
 | 
						import MongoDB from '$lib/components/svg/databases/MongoDB.svelte';
 | 
				
			||||||
 | 
						import MariaDB from '$lib/components/svg/databases/MariaDB.svelte';
 | 
				
			||||||
	import MySQL from '$lib/components/svg/databases/MySQL.svelte';
 | 
						import MySQL from '$lib/components/svg/databases/MySQL.svelte';
 | 
				
			||||||
	import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
 | 
						import PostgreSQL from '$lib/components/svg/databases/PostgreSQL.svelte';
 | 
				
			||||||
	import Redis from '$lib/components/svg/databases/Redis.svelte';
 | 
						import Redis from '$lib/components/svg/databases/Redis.svelte';
 | 
				
			||||||
@@ -46,7 +47,7 @@
 | 
				
			|||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex flex-col flex-wrap justify-center">
 | 
					<div class="flex-col justify-center">
 | 
				
			||||||
	{#if !databases || ownDatabases.length === 0}
 | 
						{#if !databases || ownDatabases.length === 0}
 | 
				
			||||||
		<div class="flex-col">
 | 
							<div class="flex-col">
 | 
				
			||||||
			<div class="text-center text-xl font-bold">{$t('database.no_databases_found')}</div>
 | 
								<div class="text-center text-xl font-bold">{$t('database.no_databases_found')}</div>
 | 
				
			||||||
@@ -66,6 +67,8 @@
 | 
				
			|||||||
								<MongoDB isAbsolute />
 | 
													<MongoDB isAbsolute />
 | 
				
			||||||
							{:else if database.type === 'mysql'}
 | 
												{:else if database.type === 'mysql'}
 | 
				
			||||||
								<MySQL isAbsolute />
 | 
													<MySQL isAbsolute />
 | 
				
			||||||
 | 
												{:else if database.type === 'mariadb'}
 | 
				
			||||||
 | 
													<MariaDB isAbsolute />
 | 
				
			||||||
							{:else if database.type === 'postgresql'}
 | 
												{:else if database.type === 'postgresql'}
 | 
				
			||||||
								<PostgreSQL isAbsolute />
 | 
													<PostgreSQL isAbsolute />
 | 
				
			||||||
							{:else if database.type === 'redis'}
 | 
												{:else if database.type === 'redis'}
 | 
				
			||||||
@@ -98,6 +101,8 @@
 | 
				
			|||||||
									<CouchDB isAbsolute />
 | 
														<CouchDB isAbsolute />
 | 
				
			||||||
								{:else if database.type === 'mongodb'}
 | 
													{:else if database.type === 'mongodb'}
 | 
				
			||||||
									<MongoDB isAbsolute />
 | 
														<MongoDB isAbsolute />
 | 
				
			||||||
 | 
													{:else if database.type === 'mariadb'}
 | 
				
			||||||
 | 
														<MariaDB isAbsolute />
 | 
				
			||||||
								{:else if database.type === 'mysql'}
 | 
													{:else if database.type === 'mysql'}
 | 
				
			||||||
									<MySQL isAbsolute />
 | 
														<MySQL isAbsolute />
 | 
				
			||||||
								{:else if database.type === 'postgresql'}
 | 
													{:else if database.type === 'postgresql'}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,8 +26,12 @@ export const get: RequestHandler = async (event) => {
 | 
				
			|||||||
			// // await saveSshKey(destination);
 | 
								// // await saveSshKey(destination);
 | 
				
			||||||
			// payload.state = await checkContainer(engine, 'coolify-haproxy');
 | 
								// payload.state = await checkContainer(engine, 'coolify-haproxy');
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								let containerName = 'coolify-proxy';
 | 
				
			||||||
 | 
								if (!settings.isTraefikUsed) {
 | 
				
			||||||
 | 
									containerName = 'coolify-haproxy';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			payload.state =
 | 
								payload.state =
 | 
				
			||||||
				destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
 | 
									destination?.engine && (await checkContainer(destination.engine, containerName));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200,
 | 
								status: 200,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,10 +39,13 @@
 | 
				
			|||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex space-x-1 p-6 text-2xl font-bold">
 | 
					<div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
 | 
				
			||||||
	<div class="tracking-tight">{$t('application.destination')}</div>
 | 
						<div class="-mb-5 flex-col">
 | 
				
			||||||
	<span class="arrow-right-applications px-1">></span>
 | 
							<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
 | 
				
			||||||
	<span class="pr-2">{destination.name}</span>
 | 
								Configuration
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<span class="text-xs">{destination.name}</span>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="mx-auto max-w-4xl px-6">
 | 
					<div class="mx-auto max-w-4xl px-6">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,12 @@
 | 
				
			|||||||
import { getUserDetails } from '$lib/common';
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { startCoolifyProxy, stopCoolifyProxy } from '$lib/haproxy';
 | 
					import {
 | 
				
			||||||
 | 
						startCoolifyProxy,
 | 
				
			||||||
 | 
						startTraefikProxy,
 | 
				
			||||||
 | 
						stopCoolifyProxy,
 | 
				
			||||||
 | 
						stopTraefikProxy
 | 
				
			||||||
 | 
					} from '$lib/haproxy';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
@@ -11,9 +16,16 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
	const { engine } = await event.request.json();
 | 
						const { engine } = await event.request.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		await stopCoolifyProxy(engine);
 | 
							const settings = await db.prisma.setting.findFirst({});
 | 
				
			||||||
		await startCoolifyProxy(engine);
 | 
							if (settings?.isTraefikUsed) {
 | 
				
			||||||
 | 
								await stopTraefikProxy(engine);
 | 
				
			||||||
 | 
								await startTraefikProxy(engine);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await stopCoolifyProxy(engine);
 | 
				
			||||||
 | 
								await startCoolifyProxy(engine);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
 | 
							await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200
 | 
								status: 200
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,12 @@
 | 
				
			|||||||
import { getUserDetails } from '$lib/common';
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import { startCoolifyProxy, stopCoolifyProxy } from '$lib/haproxy';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
						startCoolifyProxy,
 | 
				
			||||||
 | 
						startTraefikProxy,
 | 
				
			||||||
 | 
						stopCoolifyProxy,
 | 
				
			||||||
 | 
						stopTraefikProxy
 | 
				
			||||||
 | 
					} from '$lib/haproxy';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
@@ -8,14 +14,24 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
	if (status === 401) return { status, body };
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { engine } = await event.request.json();
 | 
						const { engine } = await event.request.json();
 | 
				
			||||||
 | 
						const settings = await db.prisma.setting.findFirst({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		await startCoolifyProxy(engine);
 | 
							if (settings?.isTraefikUsed) {
 | 
				
			||||||
 | 
								await startTraefikProxy(engine);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await startCoolifyProxy(engine);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200
 | 
								status: 200
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		await stopCoolifyProxy(engine);
 | 
							if (settings?.isTraefikUsed) {
 | 
				
			||||||
 | 
								await stopTraefikProxy(engine);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await stopCoolifyProxy(engine);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return ErrorHandler(error);
 | 
							return ErrorHandler(error);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { getUserDetails } from '$lib/common';
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
import { stopCoolifyProxy } from '$lib/haproxy';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { stopCoolifyProxy, stopTraefikProxy } from '$lib/haproxy';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
@@ -9,7 +10,13 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const { engine } = await event.request.json();
 | 
						const { engine } = await event.request.json();
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		await stopCoolifyProxy(engine);
 | 
							const settings = await db.prisma.setting.findFirst({});
 | 
				
			||||||
 | 
							if (settings?.isTraefikUsed) {
 | 
				
			||||||
 | 
								await stopTraefikProxy(engine);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								await stopCoolifyProxy(engine);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: 200
 | 
								status: 200
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,7 @@
 | 
				
			|||||||
		</a>
 | 
							</a>
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="flex justify-center">
 | 
					<div class="flex-col justify-center">
 | 
				
			||||||
	{#if !destinations || ownDestinations.length === 0}
 | 
						{#if !destinations || ownDestinations.length === 0}
 | 
				
			||||||
		<div class="flex-col">
 | 
							<div class="flex-col">
 | 
				
			||||||
			<div class="text-center text-xl font-bold">{$t('destination.no_destination_found')}</div>
 | 
								<div class="text-center text-xl font-bold">{$t('destination.no_destination_found')}</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
 | 
						import { get } from '$lib/api';
 | 
				
			||||||
 | 
						import { onDestroy, onMount } from 'svelte';
 | 
				
			||||||
 | 
						import Trend from './_Trend.svelte';
 | 
				
			||||||
 | 
						import { session } from '$app/stores';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let applicationsCount: number;
 | 
						export let applicationsCount: number;
 | 
				
			||||||
	export let sourcesCount: number;
 | 
						export let sourcesCount: number;
 | 
				
			||||||
@@ -28,89 +32,260 @@
 | 
				
			|||||||
	export let teamsCount: number;
 | 
						export let teamsCount: number;
 | 
				
			||||||
	export let databasesCount: number;
 | 
						export let databasesCount: number;
 | 
				
			||||||
	export let servicesCount: number;
 | 
						export let servicesCount: number;
 | 
				
			||||||
 | 
						let loading = {
 | 
				
			||||||
 | 
							usage: false
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						let usageInterval = null;
 | 
				
			||||||
 | 
						let memoryWarning = false;
 | 
				
			||||||
 | 
						let cpuWarning = false;
 | 
				
			||||||
 | 
						let diskWarning = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let trends = {
 | 
				
			||||||
 | 
							memory: 'stable',
 | 
				
			||||||
 | 
							cpu: 'stable',
 | 
				
			||||||
 | 
							disk: 'stable'
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						let usage = {
 | 
				
			||||||
 | 
							cpu: {
 | 
				
			||||||
 | 
								load: [0, 0, 0],
 | 
				
			||||||
 | 
								count: 0,
 | 
				
			||||||
 | 
								usage: 0
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							memory: {
 | 
				
			||||||
 | 
								totalMemMb: 0,
 | 
				
			||||||
 | 
								freeMemMb: 0,
 | 
				
			||||||
 | 
								usedMemMb: 0,
 | 
				
			||||||
 | 
								freeMemPercentage: 0
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							disk: {
 | 
				
			||||||
 | 
								freePercentage: 0,
 | 
				
			||||||
 | 
								totalGb: 0,
 | 
				
			||||||
 | 
								usedGb: 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						async function getStatus() {
 | 
				
			||||||
 | 
							if (loading.usage) return;
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								loading.usage = true;
 | 
				
			||||||
 | 
								const data = await get(`/dashboard.json?usage=true`);
 | 
				
			||||||
 | 
								if (data.memory.freeMemPercentage === usage.memory.freeMemPercentage) {
 | 
				
			||||||
 | 
									trends.memory = 'stable';
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (data.memory.freeMemPercentage > usage.memory.freeMemPercentage) {
 | 
				
			||||||
 | 
										trends.memory = 'up';
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										trends.memory = 'down';
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (data.cpu.usage === usage.cpu.usage) {
 | 
				
			||||||
 | 
									trends.cpu = 'stable';
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (data.cpu.usage > usage.cpu.usage) {
 | 
				
			||||||
 | 
										trends.cpu = 'up';
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										trends.cpu = 'down';
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (data.disk.freePercentage === usage.disk.freePercentage) {
 | 
				
			||||||
 | 
									trends.disk = 'stable';
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (data.disk.freePercentage > usage.disk.freePercentage) {
 | 
				
			||||||
 | 
										trends.disk = 'up';
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										trends.disk = 'down';
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								usage = data;
 | 
				
			||||||
 | 
								if (usage.memory.freeMemPercentage < 15) {
 | 
				
			||||||
 | 
									memoryWarning = true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									memoryWarning = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (usage.cpu.usage > 90) {
 | 
				
			||||||
 | 
									cpuWarning = true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									cpuWarning = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (usage.disk.freePercentage < 10) {
 | 
				
			||||||
 | 
									diskWarning = true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									diskWarning = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
							} finally {
 | 
				
			||||||
 | 
								loading.usage = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						onDestroy(() => {
 | 
				
			||||||
 | 
							clearInterval(usageInterval);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						onMount(async () => {
 | 
				
			||||||
 | 
							if ($session.teamId === '0') {
 | 
				
			||||||
 | 
								await getStatus();
 | 
				
			||||||
 | 
								usageInterval = setInterval(async () => {
 | 
				
			||||||
 | 
									await getStatus();
 | 
				
			||||||
 | 
								}, 1000);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex space-x-1 p-6 font-bold">
 | 
					<div class="flex space-x-1 p-6 font-bold">
 | 
				
			||||||
	<div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div>
 | 
						<div class="mr-4 text-2xl tracking-tight">{$t('index.dashboard')}</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class="mt-10 pb-12 tracking-tight sm:pb-16">
 | 
					<div class="mt-10 pb-12 tracking-tight sm:pb-16">
 | 
				
			||||||
	<div class="relative">
 | 
						<div class="mx-auto max-w-4xl">
 | 
				
			||||||
		<div class="absolute inset-0 h-1/2" />
 | 
							{#if $session.teamId === '0'}
 | 
				
			||||||
		<div class="relative mx-auto px-4 sm:px-6 lg:px-8">
 | 
								<div class="px-6 text-2xl font-bold">Server Usage</div>
 | 
				
			||||||
			<div class="mx-auto max-w-4xl">
 | 
								<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
				<dl class="gap-5 gap-y-16 sm:grid sm:grid-cols-3">
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
					<a
 | 
										<dt class="truncate text-sm font-medium text-white">Total Memory</dt>
 | 
				
			||||||
						href="/applications"
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
						sveltekit:prefetch
 | 
											{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-green-500 no-underline transition duration-150 hover:bg-green-500 hover:text-white"
 | 
										</dd>
 | 
				
			||||||
					>
 | 
									</div>
 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
					 | 
				
			||||||
							{$t('index.applications')}
 | 
					 | 
				
			||||||
						</dt>
 | 
					 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">
 | 
					 | 
				
			||||||
							{applicationsCount}
 | 
					 | 
				
			||||||
						</dd>
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
					<a
 | 
					 | 
				
			||||||
						href="/destinations"
 | 
					 | 
				
			||||||
						sveltekit:prefetch
 | 
					 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-sky-500 no-underline transition duration-150 hover:bg-sky-500 hover:text-white"
 | 
					 | 
				
			||||||
					>
 | 
					 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
					 | 
				
			||||||
							{$t('index.destinations')}
 | 
					 | 
				
			||||||
						</dt>
 | 
					 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">
 | 
					 | 
				
			||||||
							{destinationsCount}
 | 
					 | 
				
			||||||
						</dd>
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
					<a
 | 
					 | 
				
			||||||
						href="/sources"
 | 
					 | 
				
			||||||
						sveltekit:prefetch
 | 
					 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-orange-500 no-underline transition duration-150 hover:bg-orange-500 hover:text-white"
 | 
					 | 
				
			||||||
					>
 | 
					 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
					 | 
				
			||||||
							{$t('index.git_sources')}
 | 
					 | 
				
			||||||
						</dt>
 | 
					 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">
 | 
					 | 
				
			||||||
							{sourcesCount}
 | 
					 | 
				
			||||||
						</dd>
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
					<a
 | 
					 | 
				
			||||||
						href="/databases"
 | 
					 | 
				
			||||||
						sveltekit:prefetch
 | 
					 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-purple-500 no-underline transition duration-150 hover:bg-purple-500 hover:text-white"
 | 
					 | 
				
			||||||
					>
 | 
					 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
					 | 
				
			||||||
							{$t('index.databases')}
 | 
					 | 
				
			||||||
						</dt>
 | 
					 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">{databasesCount}</dd>
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
					<a
 | 
					 | 
				
			||||||
						href="/services"
 | 
					 | 
				
			||||||
						sveltekit:prefetch
 | 
					 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-pink-500 no-underline transition duration-150 hover:bg-pink-500 hover:text-white"
 | 
					 | 
				
			||||||
					>
 | 
					 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
					 | 
				
			||||||
							{$t('index.services')}
 | 
					 | 
				
			||||||
						</dt>
 | 
					 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">{servicesCount}</dd>
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<a
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
						href="/iam"
 | 
										<dt class="truncate text-sm font-medium text-white">Used Memory</dt>
 | 
				
			||||||
						sveltekit:prefetch
 | 
										<dd class="mt-1 text-3xl font-semibold text-white ">
 | 
				
			||||||
						class="flex cursor-pointer flex-col rounded p-6 text-center text-cyan-500 no-underline transition duration-150 hover:bg-cyan-500 hover:text-white"
 | 
											{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
 | 
				
			||||||
					>
 | 
										</dd>
 | 
				
			||||||
						<dt class="order-2 mt-2 text-sm font-bold uppercase leading-6 text-white">
 | 
									</div>
 | 
				
			||||||
							{$t('index.teams')}
 | 
					
 | 
				
			||||||
						</dt>
 | 
									<div
 | 
				
			||||||
						<dd class="order-1 text-5xl font-extrabold ">
 | 
										class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
 | 
				
			||||||
							{teamsCount}
 | 
										class:bg-red-500={memoryWarning}
 | 
				
			||||||
						</dd>
 | 
									>
 | 
				
			||||||
					</a>
 | 
										<dt class="truncate text-sm font-medium text-white">Free Memory</dt>
 | 
				
			||||||
				</dl>
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
			</div>
 | 
											{usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
 | 
				
			||||||
		</div>
 | 
											{#if !memoryWarning}
 | 
				
			||||||
 | 
												<Trend trend={trends.memory} />
 | 
				
			||||||
 | 
											{/if}
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</dl>
 | 
				
			||||||
 | 
								<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">Total CPUs</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.cpu.count}
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div
 | 
				
			||||||
 | 
										class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
										class:bg-red-500={cpuWarning}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">CPU Usage</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.cpu.usage}<span class="text-sm">%</span>
 | 
				
			||||||
 | 
											{#if !cpuWarning}
 | 
				
			||||||
 | 
												<Trend trend={trends.cpu} />
 | 
				
			||||||
 | 
											{/if}
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">Load Average (5/10/30mins)</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.cpu.load.join('/')}
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</dl>
 | 
				
			||||||
 | 
								<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">Total Disk</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.disk.totalGb}<span class="text-sm">GB</span>
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">Used Disk</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.disk.usedGb}<span class="text-sm">GB</span>
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div
 | 
				
			||||||
 | 
										class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
										class:bg-red-500={diskWarning}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<dt class="truncate text-sm font-medium text-white">Free Disk</dt>
 | 
				
			||||||
 | 
										<dd class="mt-1 text-3xl font-semibold text-white">
 | 
				
			||||||
 | 
											{usage?.disk.freePercentage}<span class="text-sm">%</span>
 | 
				
			||||||
 | 
											{#if !diskWarning}
 | 
				
			||||||
 | 
												<Trend trend={trends.disk} />
 | 
				
			||||||
 | 
											{/if}
 | 
				
			||||||
 | 
										</dd>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</dl>
 | 
				
			||||||
 | 
								<div class="px-6 pt-20 text-2xl font-bold">Resources</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
							<dl class="mt-5 grid grid-cols-1 gap-5 px-2 sm:grid-cols-3">
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/applications"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-green-500 no-underline transition-all duration-100 hover:bg-green-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.applications')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold ">
 | 
				
			||||||
 | 
										{applicationsCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/destinations"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-sky-500 no-underline transition-all duration-100 hover:bg-sky-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.destinations')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold ">
 | 
				
			||||||
 | 
										{destinationsCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/sources"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-orange-500 no-underline transition-all duration-100 hover:bg-orange-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.git_sources')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold">
 | 
				
			||||||
 | 
										{sourcesCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
							</dl>
 | 
				
			||||||
 | 
							<dl class="mt-5 grid grid-cols-1 gap-5 px-2 sm:grid-cols-3">
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/databases"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-purple-500 no-underline transition-all duration-100 hover:bg-purple-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.databases')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold ">
 | 
				
			||||||
 | 
										{databasesCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/services"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-pink-500 no-underline transition-all duration-100 hover:bg-pink-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.services')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold ">
 | 
				
			||||||
 | 
										{servicesCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<a
 | 
				
			||||||
 | 
									href="/iam"
 | 
				
			||||||
 | 
									sveltekit:prefetch
 | 
				
			||||||
 | 
									class="overflow-hidden rounded px-4 py-5 text-center text-cyan-500 no-underline transition-all duration-100 hover:bg-cyan-500 hover:text-white sm:p-6 sm:text-left"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<dt class="truncate text-sm font-medium text-white">{$t('index.teams')}</dt>
 | 
				
			||||||
 | 
									<dd class="mt-1 text-3xl font-semibold ">
 | 
				
			||||||
 | 
										{teamsCount}
 | 
				
			||||||
 | 
									</dd>
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
							</dl>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,14 +30,16 @@
 | 
				
			|||||||
		value={service.minio.rootUserPassword}
 | 
							value={service.minio.rootUserPassword}
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					{#if !service.minio.apiFqdn}
 | 
				
			||||||
	<label for="publicPort">{$t('forms.api_port')}</label>
 | 
						<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<input
 | 
							<label for="publicPort">{$t('forms.api_port')}</label>
 | 
				
			||||||
		name="publicPort"
 | 
							<input
 | 
				
			||||||
		id="publicPort"
 | 
								name="publicPort"
 | 
				
			||||||
		value={service.minio.publicPort}
 | 
								id="publicPort"
 | 
				
			||||||
		disabled
 | 
								value={service.minio.publicPort}
 | 
				
			||||||
		readonly
 | 
								disabled
 | 
				
			||||||
		placeholder={$t('forms.generated_automatically_after_start')}
 | 
								readonly
 | 
				
			||||||
	/>
 | 
								placeholder={$t('forms.generated_automatically_after_start')}
 | 
				
			||||||
</div>
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,32 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import { session } from '$app/stores';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
 | 
						import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
 | 
				
			||||||
 | 
						import Explainer from '$lib/components/Explainer.svelte';
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
	export let service;
 | 
						export let service;
 | 
				
			||||||
	export let readOnly;
 | 
						export let readOnly;
 | 
				
			||||||
 | 
						export let isRunning;
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex space-x-1 py-5 font-bold">
 | 
					<div class="flex space-x-1 py-5 font-bold">
 | 
				
			||||||
	<div class="title">Plausible Analytics</div>
 | 
						<div class="title">Plausible Analytics</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
 | 
						<label for="scriptName">Script Name</label>
 | 
				
			||||||
 | 
						<input
 | 
				
			||||||
 | 
							name="scriptName"
 | 
				
			||||||
 | 
							id="scriptName"
 | 
				
			||||||
 | 
							readonly={!$session.isAdmin && !isRunning}
 | 
				
			||||||
 | 
							disabled={!$session.isAdmin || isRunning}
 | 
				
			||||||
 | 
							placeholder="plausible.js"
 | 
				
			||||||
 | 
							bind:value={service.plausibleAnalytics.scriptName}
 | 
				
			||||||
 | 
							required
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
 | 
						<Explainer
 | 
				
			||||||
 | 
							text="Useful if you would like to rename the collector script to prevent it blocked by AdBlockers."
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<label for="email">{$t('forms.email')}</label>
 | 
						<label for="email">{$t('forms.email')}</label>
 | 
				
			||||||
	<input
 | 
						<input
 | 
				
			||||||
@@ -77,16 +96,3 @@
 | 
				
			|||||||
		disabled
 | 
							disabled
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<!-- <div class="grid grid-cols-3 items-center">
 | 
					 | 
				
			||||||
	<label for="postgresqlPublicPort">Public Port</label>
 | 
					 | 
				
			||||||
	<div class="col-span-2 ">
 | 
					 | 
				
			||||||
		<CopyPasswordField
 | 
					 | 
				
			||||||
			placeholder="{ $t('forms.generated_automatically_after_start') }"
 | 
					 | 
				
			||||||
			readonly
 | 
					 | 
				
			||||||
			disabled
 | 
					 | 
				
			||||||
			id="postgresqlPublicPort"
 | 
					 | 
				
			||||||
			name="postgresqlPublicPort"
 | 
					 | 
				
			||||||
			value={service.plausibleAnalytics.postgresqlPublicPort}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div> -->
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import { browser } from '$app/env';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let service;
 | 
						export let service;
 | 
				
			||||||
	export let isRunning;
 | 
						export let isRunning;
 | 
				
			||||||
	export let readOnly;
 | 
						export let readOnly;
 | 
				
			||||||
@@ -12,6 +14,8 @@
 | 
				
			|||||||
	import { errorNotification } from '$lib/form';
 | 
						import { errorNotification } from '$lib/form';
 | 
				
			||||||
	import { t } from '$lib/translations';
 | 
						import { t } from '$lib/translations';
 | 
				
			||||||
	import { toast } from '@zerodevx/svelte-toast';
 | 
						import { toast } from '@zerodevx/svelte-toast';
 | 
				
			||||||
 | 
						import cuid from 'cuid';
 | 
				
			||||||
 | 
						import { onMount } from 'svelte';
 | 
				
			||||||
	import Fider from './_Fider.svelte';
 | 
						import Fider from './_Fider.svelte';
 | 
				
			||||||
	import Ghost from './_Ghost.svelte';
 | 
						import Ghost from './_Ghost.svelte';
 | 
				
			||||||
	import Hasura from './_Hasura.svelte';
 | 
						import Hasura from './_Hasura.svelte';
 | 
				
			||||||
@@ -29,9 +33,14 @@
 | 
				
			|||||||
	let dualCerts = service.dualCerts;
 | 
						let dualCerts = service.dualCerts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function handleSubmit() {
 | 
						async function handleSubmit() {
 | 
				
			||||||
 | 
							if (loading) return;
 | 
				
			||||||
		loading = true;
 | 
							loading = true;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			await post(`/services/${id}/check.json`, { fqdn: service.fqdn });
 | 
								await post(`/services/${id}/check.json`, {
 | 
				
			||||||
 | 
									fqdn: service.fqdn,
 | 
				
			||||||
 | 
									otherFqdns: [service.minio.apiFqdn],
 | 
				
			||||||
 | 
									exposePort: service.exposePort
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
			await post(`/services/${id}/${service.type}.json`, { ...service });
 | 
								await post(`/services/${id}/${service.type}.json`, { ...service });
 | 
				
			||||||
			return window.location.reload();
 | 
								return window.location.reload();
 | 
				
			||||||
		} catch ({ error }) {
 | 
							} catch ({ error }) {
 | 
				
			||||||
@@ -62,6 +71,12 @@
 | 
				
			|||||||
			return errorNotification(error);
 | 
								return errorNotification(error);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						onMount(async () => {
 | 
				
			||||||
 | 
							if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
 | 
				
			||||||
 | 
								service.fqdn = `http://${cuid()}.demo.coolify.io`;
 | 
				
			||||||
 | 
								await post(`/services/${id}/${service.type}.json`, { ...service });
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="mx-auto max-w-4xl px-6 pb-12">
 | 
					<div class="mx-auto max-w-4xl px-6 pb-12">
 | 
				
			||||||
@@ -86,6 +101,14 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="grid grid-flow-row gap-2">
 | 
							<div class="grid grid-flow-row gap-2">
 | 
				
			||||||
 | 
								{#if service.type === 'minio' && !service.minio.apiFqdn && isRunning}
 | 
				
			||||||
 | 
									<div class="text-center">
 | 
				
			||||||
 | 
										<span class="font-bold text-red-500">IMPORTANT!</span> There was a small modification with
 | 
				
			||||||
 | 
										Minio in the latest version of Coolify. Now you can separate the Console URL from the API URL,
 | 
				
			||||||
 | 
										so you could use both through SSL. But this proccess cannot be done automatically, so you have
 | 
				
			||||||
 | 
										to stop your Minio instance, configure the new domain and start it back. Sorry for any inconvenience.
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
			<div class="mt-2 grid grid-cols-2 items-center px-10">
 | 
								<div class="mt-2 grid grid-cols-2 items-center px-10">
 | 
				
			||||||
				<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
 | 
									<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
 | 
				
			||||||
				<div>
 | 
									<div>
 | 
				
			||||||
@@ -131,25 +154,62 @@
 | 
				
			|||||||
					{/if}
 | 
										{/if}
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<div class="grid grid-cols-2 px-10">
 | 
								{#if service.type === 'minio'}
 | 
				
			||||||
				<div class="flex-col ">
 | 
									<div class="grid grid-cols-2 px-10">
 | 
				
			||||||
					<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
 | 
										<div class="flex-col ">
 | 
				
			||||||
						>{$t('application.url_fqdn')}</label
 | 
											<label for="fqdn" class="pt-2 text-base font-bold text-stone-100">Console URL</label>
 | 
				
			||||||
					>
 | 
										</div>
 | 
				
			||||||
					<Explainer text={$t('application.https_explainer')} />
 | 
					
 | 
				
			||||||
				</div>
 | 
										<CopyPasswordField
 | 
				
			||||||
 | 
											placeholder="eg: https://console.min.io"
 | 
				
			||||||
 | 
											readonly={!$session.isAdmin && !isRunning}
 | 
				
			||||||
 | 
											disabled={!$session.isAdmin || isRunning}
 | 
				
			||||||
 | 
											name="fqdn"
 | 
				
			||||||
 | 
											id="fqdn"
 | 
				
			||||||
 | 
											pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
				
			||||||
 | 
											bind:value={service.fqdn}
 | 
				
			||||||
 | 
											required
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="grid grid-cols-2 px-10">
 | 
				
			||||||
 | 
										<div class="flex-col ">
 | 
				
			||||||
 | 
											<label for="apiFqdn" class="pt-2 text-base font-bold text-stone-100">API URL</label>
 | 
				
			||||||
 | 
											<Explainer text={$t('application.https_explainer')} />
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<CopyPasswordField
 | 
				
			||||||
 | 
											placeholder="eg: https://min.io"
 | 
				
			||||||
 | 
											readonly={!$session.isAdmin && !isRunning}
 | 
				
			||||||
 | 
											disabled={!$session.isAdmin || isRunning}
 | 
				
			||||||
 | 
											name="apiFqdn"
 | 
				
			||||||
 | 
											id="apiFqdn"
 | 
				
			||||||
 | 
											pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
				
			||||||
 | 
											bind:value={service.minio.apiFqdn}
 | 
				
			||||||
 | 
											required
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{:else}
 | 
				
			||||||
 | 
									<div class="grid grid-cols-2 px-10">
 | 
				
			||||||
 | 
										<div class="flex-col ">
 | 
				
			||||||
 | 
											<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
 | 
				
			||||||
 | 
												>{$t('application.url_fqdn')}</label
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
											<Explainer text={$t('application.https_explainer')} />
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<CopyPasswordField
 | 
				
			||||||
 | 
											placeholder="eg: https://analytics.coollabs.io"
 | 
				
			||||||
 | 
											readonly={!$session.isAdmin && !isRunning}
 | 
				
			||||||
 | 
											disabled={!$session.isAdmin || isRunning}
 | 
				
			||||||
 | 
											name="fqdn"
 | 
				
			||||||
 | 
											id="fqdn"
 | 
				
			||||||
 | 
											pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
				
			||||||
 | 
											bind:value={service.fqdn}
 | 
				
			||||||
 | 
											required
 | 
				
			||||||
 | 
										/>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<CopyPasswordField
 | 
					 | 
				
			||||||
					placeholder="eg: https://analytics.coollabs.io"
 | 
					 | 
				
			||||||
					readonly={!$session.isAdmin && !isRunning}
 | 
					 | 
				
			||||||
					disabled={!$session.isAdmin || isRunning}
 | 
					 | 
				
			||||||
					name="fqdn"
 | 
					 | 
				
			||||||
					id="fqdn"
 | 
					 | 
				
			||||||
					pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
 | 
					 | 
				
			||||||
					bind:value={service.fqdn}
 | 
					 | 
				
			||||||
					required
 | 
					 | 
				
			||||||
				/>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			<div class="grid grid-cols-2 items-center px-10">
 | 
								<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
				<Setting
 | 
									<Setting
 | 
				
			||||||
					disabled={isRunning}
 | 
										disabled={isRunning}
 | 
				
			||||||
@@ -160,8 +220,23 @@
 | 
				
			|||||||
					on:click={() => !isRunning && changeSettings('dualCerts')}
 | 
										on:click={() => !isRunning && changeSettings('dualCerts')}
 | 
				
			||||||
				/>
 | 
									/>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
 | 
									<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
 | 
				
			||||||
 | 
									<input
 | 
				
			||||||
 | 
										readonly={!$session.isAdmin && !isRunning}
 | 
				
			||||||
 | 
										disabled={!$session.isAdmin || isRunning}
 | 
				
			||||||
 | 
										name="exposePort"
 | 
				
			||||||
 | 
										id="exposePort"
 | 
				
			||||||
 | 
										bind:value={service.exposePort}
 | 
				
			||||||
 | 
										placeholder="12345"
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<Explainer
 | 
				
			||||||
 | 
										text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			{#if service.type === 'plausibleanalytics'}
 | 
								{#if service.type === 'plausibleanalytics'}
 | 
				
			||||||
				<PlausibleAnalytics bind:service {readOnly} />
 | 
									<PlausibleAnalytics bind:service {isRunning} {readOnly} />
 | 
				
			||||||
			{:else if service.type === 'minio'}
 | 
								{:else if service.type === 'minio'}
 | 
				
			||||||
				<MinIo {service} />
 | 
									<MinIo {service} />
 | 
				
			||||||
			{:else if service.type === 'vscodeserver'}
 | 
								{:else if service.type === 'vscodeserver'}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@
 | 
				
			|||||||
	let ftpUser = service.wordpress.ftpUser;
 | 
						let ftpUser = service.wordpress.ftpUser;
 | 
				
			||||||
	let ftpPassword = service.wordpress.ftpPassword;
 | 
						let ftpPassword = service.wordpress.ftpPassword;
 | 
				
			||||||
	let ftpLoading = false;
 | 
						let ftpLoading = false;
 | 
				
			||||||
 | 
						let ownMysql = service.wordpress.ownMysql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function generateUrl(publicPort) {
 | 
						function generateUrl(publicPort) {
 | 
				
			||||||
		return browser
 | 
							return browser
 | 
				
			||||||
@@ -40,7 +41,7 @@
 | 
				
			|||||||
					publicPort,
 | 
										publicPort,
 | 
				
			||||||
					ftpUser: user,
 | 
										ftpUser: user,
 | 
				
			||||||
					ftpPassword: password
 | 
										ftpPassword: password
 | 
				
			||||||
				} = await post(`/services/${id}/wordpress/settings.json`, {
 | 
									} = await post(`/services/${id}/wordpress/ftp.json`, {
 | 
				
			||||||
					ftpEnabled
 | 
										ftpEnabled
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
				ftpUrl = generateUrl(publicPort);
 | 
									ftpUrl = generateUrl(publicPort);
 | 
				
			||||||
@@ -52,6 +53,18 @@
 | 
				
			|||||||
			} finally {
 | 
								} finally {
 | 
				
			||||||
				ftpLoading = false;
 | 
									ftpLoading = false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									if (name === 'ownMysql') {
 | 
				
			||||||
 | 
										ownMysql = !ownMysql;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									await post(`/services/${id}/wordpress/settings.json`, {
 | 
				
			||||||
 | 
										ownMysql
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									service.wordpress.ownMysql = ownMysql;
 | 
				
			||||||
 | 
								} catch ({ error }) {
 | 
				
			||||||
 | 
									return errorNotification(error);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
@@ -106,52 +119,96 @@ define('SUBDOMAIN_INSTALL', false);`
 | 
				
			|||||||
<div class="flex space-x-1 py-5 font-bold">
 | 
					<div class="flex space-x-1 py-5 font-bold">
 | 
				
			||||||
	<div class="title">MySQL</div>
 | 
						<div class="title">MySQL</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
 | 
						<Setting
 | 
				
			||||||
 | 
							dataTooltip={$t('forms.must_be_stopped_to_modify')}
 | 
				
			||||||
 | 
							bind:setting={service.wordpress.ownMysql}
 | 
				
			||||||
 | 
							disabled={isRunning}
 | 
				
			||||||
 | 
							on:click={() => !isRunning && changeSettings('ownMysql')}
 | 
				
			||||||
 | 
							title="Use your own MySQL server"
 | 
				
			||||||
 | 
							description="Enables the use of your own MySQL server. If you don't have one, you can use the one provided by Coolify."
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{#if service.wordpress.ownMysql}
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
 | 
							<label for="mysqlHost">Host</label>
 | 
				
			||||||
 | 
							<input
 | 
				
			||||||
 | 
								name="mysqlHost"
 | 
				
			||||||
 | 
								id="mysqlHost"
 | 
				
			||||||
 | 
								required
 | 
				
			||||||
 | 
								readonly={isRunning}
 | 
				
			||||||
 | 
								disabled={isRunning}
 | 
				
			||||||
 | 
								bind:value={service.wordpress.mysqlHost}
 | 
				
			||||||
 | 
								placeholder="{$t('forms.eg')}: db.coolify.io"
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
 | 
							<label for="mysqlPort">Port</label>
 | 
				
			||||||
 | 
							<input
 | 
				
			||||||
 | 
								name="mysqlPort"
 | 
				
			||||||
 | 
								id="mysqlPort"
 | 
				
			||||||
 | 
								required
 | 
				
			||||||
 | 
								readonly={isRunning}
 | 
				
			||||||
 | 
								disabled={isRunning}
 | 
				
			||||||
 | 
								bind:value={service.wordpress.mysqlPort}
 | 
				
			||||||
 | 
								placeholder="{$t('forms.eg')}: 3306"
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<label for="mysqlDatabase">{$t('index.database')}</label>
 | 
						<label for="mysqlDatabase">{$t('index.database')}</label>
 | 
				
			||||||
	<input
 | 
						<input
 | 
				
			||||||
		name="mysqlDatabase"
 | 
							name="mysqlDatabase"
 | 
				
			||||||
		id="mysqlDatabase"
 | 
							id="mysqlDatabase"
 | 
				
			||||||
		required
 | 
							required
 | 
				
			||||||
		readonly={readOnly}
 | 
							readonly={readOnly && !service.wordpress.ownMysql}
 | 
				
			||||||
		disabled={readOnly}
 | 
							disabled={readOnly && !service.wordpress.ownMysql}
 | 
				
			||||||
		bind:value={service.wordpress.mysqlDatabase}
 | 
							bind:value={service.wordpress.mysqlDatabase}
 | 
				
			||||||
		placeholder="{$t('forms.eg')}: wordpress_db"
 | 
							placeholder="{$t('forms.eg')}: wordpress_db"
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					{#if !service.wordpress.ownMysql}
 | 
				
			||||||
	<label for="mysqlRootUser">{$t('forms.root_user')}</label>
 | 
						<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<input
 | 
							<label for="mysqlRootUser">{$t('forms.root_user')}</label>
 | 
				
			||||||
		name="mysqlRootUser"
 | 
							<input
 | 
				
			||||||
		id="mysqlRootUser"
 | 
								name="mysqlRootUser"
 | 
				
			||||||
		placeholder="MySQL {$t('forms.root_user')}"
 | 
								id="mysqlRootUser"
 | 
				
			||||||
		value={service.wordpress.mysqlRootUser}
 | 
								placeholder="MySQL {$t('forms.root_user')}"
 | 
				
			||||||
		disabled
 | 
								value={service.wordpress.mysqlRootUser}
 | 
				
			||||||
		readonly
 | 
								readonly={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
	/>
 | 
								disabled={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
</div>
 | 
							/>
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
						</div>
 | 
				
			||||||
	<label for="mysqlRootUserPassword">{$t('forms.roots_password')}</label>
 | 
						<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<CopyPasswordField
 | 
							<label for="mysqlRootUserPassword">{$t('forms.roots_password')}</label>
 | 
				
			||||||
		id="mysqlRootUserPassword"
 | 
							<CopyPasswordField
 | 
				
			||||||
		isPasswordField
 | 
								id="mysqlRootUserPassword"
 | 
				
			||||||
		readonly
 | 
								isPasswordField
 | 
				
			||||||
		disabled
 | 
								readonly={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
		name="mysqlRootUserPassword"
 | 
								disabled={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
		value={service.wordpress.mysqlRootUserPassword}
 | 
								name="mysqlRootUserPassword"
 | 
				
			||||||
	/>
 | 
								value={service.wordpress.mysqlRootUserPassword}
 | 
				
			||||||
</div>
 | 
							/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<label for="mysqlUser">{$t('forms.user')}</label>
 | 
						<label for="mysqlUser">{$t('forms.user')}</label>
 | 
				
			||||||
	<input name="mysqlUser" id="mysqlUser" value={service.wordpress.mysqlUser} disabled readonly />
 | 
						<input
 | 
				
			||||||
 | 
							name="mysqlUser"
 | 
				
			||||||
 | 
							id="mysqlUser"
 | 
				
			||||||
 | 
							bind:value={service.wordpress.mysqlUser}
 | 
				
			||||||
 | 
							readonly={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
 | 
							disabled={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="grid grid-cols-2 items-center px-10">
 | 
					<div class="grid grid-cols-2 items-center px-10">
 | 
				
			||||||
	<label for="mysqlPassword">{$t('forms.password')}</label>
 | 
						<label for="mysqlPassword">{$t('forms.password')}</label>
 | 
				
			||||||
	<CopyPasswordField
 | 
						<CopyPasswordField
 | 
				
			||||||
		id="mysqlPassword"
 | 
							id="mysqlPassword"
 | 
				
			||||||
		isPasswordField
 | 
							isPasswordField
 | 
				
			||||||
		readonly
 | 
							readonly={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
		disabled
 | 
							disabled={isRunning || !service.wordpress.ownMysql}
 | 
				
			||||||
		name="mysqlPassword"
 | 
							name="mysqlPassword"
 | 
				
			||||||
		value={service.wordpress.mysqlPassword}
 | 
							bind:value={service.wordpress.mysqlPassword}
 | 
				
			||||||
	/>
 | 
						/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/routes/services/[id]/appwrite-wip/index.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/routes/services/[id]/appwrite-wip/index.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import { getUserDetails } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let { name, fqdn, exposePort } = await event.request.json();
 | 
				
			||||||
 | 
						if (fqdn) fqdn = fqdn.toLowerCase();
 | 
				
			||||||
 | 
						if (exposePort) exposePort = Number(exposePort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							await db.updateService({ id, fqdn, name, exposePort });
 | 
				
			||||||
 | 
							return { status: 201 };
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										519
									
								
								src/routes/services/[id]/appwrite-wip/start.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								src/routes/services/[id]/appwrite-wip/start.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,519 @@
 | 
				
			|||||||
 | 
					import { asyncExecShell, createDirectories, getEngine, getUserDetails } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { promises as fs } from 'fs';
 | 
				
			||||||
 | 
					import yaml from 'js-yaml';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import { ErrorHandler, getServiceImage } from '$lib/database';
 | 
				
			||||||
 | 
					import { makeLabelForServices } from '$lib/buildPacks/common';
 | 
				
			||||||
 | 
					import type { ComposeFile } from '$lib/types/composeFile';
 | 
				
			||||||
 | 
					import { getServiceMainPort } from '$lib/components/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const service = await db.getService({ id, teamId });
 | 
				
			||||||
 | 
							const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
 | 
				
			||||||
 | 
								service;
 | 
				
			||||||
 | 
							const network = destinationDockerId && destinationDocker.network;
 | 
				
			||||||
 | 
							const host = getEngine(destinationDocker.engine);
 | 
				
			||||||
 | 
							const port = getServiceMainPort('n8n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const { workdir } = await createDirectories({ repository: type, buildId: id });
 | 
				
			||||||
 | 
							const image = getServiceImage(type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (serviceSecret.length > 0) {
 | 
				
			||||||
 | 
								serviceSecret.forEach((secret) => {
 | 
				
			||||||
 | 
									variables[secret.name] = secret.value;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const variables = {
 | 
				
			||||||
 | 
								_APP_ENV: 'production',
 | 
				
			||||||
 | 
								_APP_VERSION: '',
 | 
				
			||||||
 | 
								_APP_LOCALE: '',
 | 
				
			||||||
 | 
								_APP_OPTIONS_ABUSE: '',
 | 
				
			||||||
 | 
								_APP_OPTIONS_FORCE_HTTPS: '',
 | 
				
			||||||
 | 
								_APP_OPENSSL_KEY_V1: '',
 | 
				
			||||||
 | 
								_APP_DOMAIN: '',
 | 
				
			||||||
 | 
								_APP_DOMAIN_TARGET: '',
 | 
				
			||||||
 | 
								_APP_CONSOLE_WHITELIST_ROOT: '',
 | 
				
			||||||
 | 
								_APP_CONSOLE_WHITELIST_EMAILS: '',
 | 
				
			||||||
 | 
								_APP_CONSOLE_WHITELIST_IPS: '',
 | 
				
			||||||
 | 
								_APP_SYSTEM_EMAIL_NAME: '',
 | 
				
			||||||
 | 
								_APP_SYSTEM_EMAIL_ADDRESS: '',
 | 
				
			||||||
 | 
								_APP_SYSTEM_RESPONSE_FORMAT: '',
 | 
				
			||||||
 | 
								_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: '',
 | 
				
			||||||
 | 
								_APP_USAGE_STATS: '',
 | 
				
			||||||
 | 
								_APP_LOGGING_PROVIDER: '',
 | 
				
			||||||
 | 
								_APP_LOGGING_CONFIG: '',
 | 
				
			||||||
 | 
								_APP_USAGE_AGGREGATION_INTERVAL: '',
 | 
				
			||||||
 | 
								_APP_WORKER_PER_CORE: '',
 | 
				
			||||||
 | 
								_APP_REDIS_HOST: '',
 | 
				
			||||||
 | 
								_APP_REDIS_PORT: '',
 | 
				
			||||||
 | 
								_APP_REDIS_USER: '',
 | 
				
			||||||
 | 
								_APP_REDIS_PASS: '',
 | 
				
			||||||
 | 
								_APP_DB_HOST: '',
 | 
				
			||||||
 | 
								_APP_DB_PORT: '',
 | 
				
			||||||
 | 
								_APP_DB_SCHEMA: '',
 | 
				
			||||||
 | 
								_APP_DB_USER: '',
 | 
				
			||||||
 | 
								_APP_DB_PASS: '',
 | 
				
			||||||
 | 
								_APP_DB_ROOT_PASS: '',
 | 
				
			||||||
 | 
								_APP_INFLUXDB_HOST: '',
 | 
				
			||||||
 | 
								_APP_INFLUXDB_PORT: '',
 | 
				
			||||||
 | 
								_APP_STATSD_HOST: '',
 | 
				
			||||||
 | 
								_APP_STATSD_PORT: '',
 | 
				
			||||||
 | 
								_APP_SMTP_HOST: '',
 | 
				
			||||||
 | 
								_APP_SMTP_PORT: '',
 | 
				
			||||||
 | 
								_APP_SMTP_SECURE: '',
 | 
				
			||||||
 | 
								_APP_SMTP_USERNAME: '',
 | 
				
			||||||
 | 
								_APP_SMTP_PASSWORD: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_LIMIT: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_ANTIVIRUS: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_ANTIVIRUS_HOST: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_ANTIVIRUS_PORT: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_DEVICE: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_S3_ACCESS_KEY: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_S3_SECRET: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_S3_REGION: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_S3_BUCKET: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_DO_SPACES_ACCESS_KEY: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_DO_SPACES_SECRET: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_DO_SPACES_REGION: '',
 | 
				
			||||||
 | 
								_APP_STORAGE_DO_SPACES_BUCKET: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_SIZE_LIMIT: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_TIMEOUT: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_BUILD_TIMEOUT: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_CONTAINERS: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_CPUS: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_MEMORY: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_MEMORY_SWAP: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_RUNTIMES: '',
 | 
				
			||||||
 | 
								_APP_EXECUTOR_SECRET: '',
 | 
				
			||||||
 | 
								_APP_EXECUTOR_RUNTIME_NETWORK: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_ENVS: '',
 | 
				
			||||||
 | 
								_APP_FUNCTIONS_INACTIVE_THRESHOLD: '',
 | 
				
			||||||
 | 
								DOCKERHUB_PULL_USERNAME: '',
 | 
				
			||||||
 | 
								DOCKERHUB_PULL_PASSWORD: '',
 | 
				
			||||||
 | 
								DOCKERHUB_PULL_EMAIL: '',
 | 
				
			||||||
 | 
								_APP_MAINTENANCE_INTERVAL: '',
 | 
				
			||||||
 | 
								_APP_MAINTENANCE_RETENTION_EXECUTION: '',
 | 
				
			||||||
 | 
								_APP_MAINTENANCE_RETENTION_ABUSE: '',
 | 
				
			||||||
 | 
								_APP_MAINTENANCE_RETENTION_AUDIT: ''
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							const config = {
 | 
				
			||||||
 | 
								appwrite: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									volumes: [
 | 
				
			||||||
 | 
										`${id}-appwrite-uploads:/storage/uploads`,
 | 
				
			||||||
 | 
										`${id}-appwrite-cache:/storage/cache`,
 | 
				
			||||||
 | 
										`${id}-appwrite-config:/storage/config`,
 | 
				
			||||||
 | 
										`${id}-appwrite-certificates:/storage/certificates`,
 | 
				
			||||||
 | 
										`${id}-appwrite-functions:/storage/functions`
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_WORKER_PER_CORE: variables._APP_WORKER_PER_CORE,
 | 
				
			||||||
 | 
										_APP_LOCALE: variables._APP_LOCALE,
 | 
				
			||||||
 | 
										_APP_CONSOLE_WHITELIST_ROOT: variables._APP_CONSOLE_WHITELIST_ROOT,
 | 
				
			||||||
 | 
										_APP_CONSOLE_WHITELIST_EMAILS: variables._APP_CONSOLE_WHITELIST_EMAILS,
 | 
				
			||||||
 | 
										_APP_CONSOLE_WHITELIST_IPS: variables._APP_CONSOLE_WHITELIST_IPS,
 | 
				
			||||||
 | 
										_APP_SYSTEM_EMAIL_NAME: variables._APP_SYSTEM_EMAIL_NAME,
 | 
				
			||||||
 | 
										_APP_SYSTEM_EMAIL_ADDRESS: variables._APP_SYSTEM_EMAIL_ADDRESS,
 | 
				
			||||||
 | 
										_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
 | 
				
			||||||
 | 
										_APP_SYSTEM_RESPONSE_FORMAT: variables._APP_SYSTEM_RESPONSE_FORMAT,
 | 
				
			||||||
 | 
										_APP_OPTIONS_ABUSE: variables._APP_OPTIONS_ABUSE,
 | 
				
			||||||
 | 
										_APP_OPTIONS_FORCE_HTTPS: variables._APP_OPTIONS_FORCE_HTTPS,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_DOMAIN: variables._APP_DOMAIN,
 | 
				
			||||||
 | 
										_APP_DOMAIN_TARGET: variables._APP_DOMAIN_TARGET,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_SMTP_HOST: variables._APP_SMTP_HOST,
 | 
				
			||||||
 | 
										_APP_SMTP_PORT: variables._APP_SMTP_PORT,
 | 
				
			||||||
 | 
										_APP_SMTP_SECURE: variables._APP_SMTP_SECURE,
 | 
				
			||||||
 | 
										_APP_SMTP_USERNAME: variables._APP_SMTP_USERNAME,
 | 
				
			||||||
 | 
										_APP_SMTP_PASSWORD: variables._APP_SMTP_PASSWORD,
 | 
				
			||||||
 | 
										_APP_USAGE_STATS: variables._APP_USAGE_STATS,
 | 
				
			||||||
 | 
										_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
 | 
				
			||||||
 | 
										_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT,
 | 
				
			||||||
 | 
										_APP_STORAGE_LIMIT: variables._APP_STORAGE_LIMIT,
 | 
				
			||||||
 | 
										_APP_STORAGE_ANTIVIRUS: variables._APP_STORAGE_ANTIVIRUS,
 | 
				
			||||||
 | 
										_APP_STORAGE_ANTIVIRUS_HOST: variables._APP_STORAGE_ANTIVIRUS_HOST,
 | 
				
			||||||
 | 
										_APP_STORAGE_ANTIVIRUS_PORT: variables._APP_STORAGE_ANTIVIRUS_PORT,
 | 
				
			||||||
 | 
										_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_SIZE_LIMIT: variables._APP_FUNCTIONS_SIZE_LIMIT,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_BUILD_TIMEOUT: variables._APP_FUNCTIONS_BUILD_TIMEOUT,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_CONTAINERS: variables._APP_FUNCTIONS_CONTAINERS,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_CPUS: variables._APP_FUNCTIONS_CPUS,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_MEMORY: variables._APP_FUNCTIONS_MEMORY,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_MEMORY_SWAP: variables._APP_FUNCTIONS_MEMORY_SWAP,
 | 
				
			||||||
 | 
										_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_RUNTIMES: variables._APP_FUNCTIONS_RUNTIMES,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG,
 | 
				
			||||||
 | 
										_APP_STATSD_HOST: variables._APP_STATSD_HOST,
 | 
				
			||||||
 | 
										_APP_STATSD_PORT: variables._APP_STATSD_PORT,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_INTERVAL: variables._APP_MAINTENANCE_INTERVAL,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_EXECUTION: variables._APP_MAINTENANCE_RETENTION_EXECUTION,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_ABUSE: variables._APP_MAINTENANCE_RETENTION_ABUSE,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_AUDIT: variables._APP_MAINTENANCE_RETENTION_AUDIT
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteRealtime: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_WORKER_PER_CORE: variables._APP_WORKER_PER_CORE,
 | 
				
			||||||
 | 
										_APP_OPTIONS_ABUSE: variables._APP_OPTIONS_ABUSE,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_USAGE_STATS: variables._APP_USAGE_STATS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteExecutor: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									volumes: [
 | 
				
			||||||
 | 
										`${id}-appwrite-functions:/storage/functions`,
 | 
				
			||||||
 | 
										`/tmp:/tmp`,
 | 
				
			||||||
 | 
										'/var/run/docker.sock:/var/run/docker.sock'
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										DOCKERHUB_PULL_USERNAME: variables.DOCKERHUB_PULL_USERNAME,
 | 
				
			||||||
 | 
										DOCKERHUB_PULL_PASSWORD: variables.DOCKERHUB_PULL_PASSWORD,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG,
 | 
				
			||||||
 | 
										_APP_VERSION: variables._APP_VERSION,
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_CPUS: variables._APP_FUNCTIONS_CPUS,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_MEMORY: variables._APP_FUNCTIONS_MEMORY,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_MEMORY_SWAP: variables._APP_FUNCTIONS_MEMORY_SWAP,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
 | 
				
			||||||
 | 
										_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_RUNTIMES: variables._APP_FUNCTIONS_RUNTIMES,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_INACTIVE_THRESHOLD: variables._APP_FUNCTIONS_INACTIVE_THRESHOLD,
 | 
				
			||||||
 | 
										_APP_EXECUTOR_RUNTIME_NETWORK: variables._APP_EXECUTOR_RUNTIME_NETWORK
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerDatabase: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerBuilds: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerAudits: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerWebhooks: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerDeletes: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									volumes: [
 | 
				
			||||||
 | 
										`${id}-appwrite-uploads:/storage/uploads`,
 | 
				
			||||||
 | 
										`${id}-appwrite-cache:/storage/cache`,
 | 
				
			||||||
 | 
										`${id}-appwrite-certificates:/storage/certificates`
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
 | 
				
			||||||
 | 
										_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerCertificates: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									volumes: [
 | 
				
			||||||
 | 
										`${id}-appwrite-config:/storage/config`,
 | 
				
			||||||
 | 
										`${id}-appwrite-certificates:/storage/certificates`
 | 
				
			||||||
 | 
									],
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DOMAIN_TARGET: variables._APP_DOMAIN_TARGET,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerFunctions: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									envvironmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
 | 
				
			||||||
 | 
										_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
 | 
				
			||||||
 | 
										_APP_USAGE_STATS: variables._APP_USAGE_STATS,
 | 
				
			||||||
 | 
										DOCKERHUB_PULL_USERNAME: variables.DOCKERHUB_PULL_USERNAME,
 | 
				
			||||||
 | 
										DOCKERHUB_PULL_PASSWORD: variables.DOCKERHUB_PULL_PASSWORD
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteWorkerMails: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_SYSTEM_EMAIL_NAME: variables._APP_SYSTEM_EMAIL_NAME,
 | 
				
			||||||
 | 
										_APP_SYSTEM_EMAIL_ADDRESS: variables._APP_SYSTEM_EMAIL_ADDRESS,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_SMTP_HOST: variables._APP_SMTP_HOST,
 | 
				
			||||||
 | 
										_APP_SMTP_PORT: variables._APP_SMTP_PORT,
 | 
				
			||||||
 | 
										_APP_SMTP_SECURE: variables._APP_SMTP_SECURE,
 | 
				
			||||||
 | 
										_APP_SMTP_USERNAME: variables._APP_SMTP_USERNAME,
 | 
				
			||||||
 | 
										_APP_SMTP_PASSWORD: variables._APP_SMTP_PASSWORD,
 | 
				
			||||||
 | 
										_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
 | 
				
			||||||
 | 
										_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteMaintenance: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_INTERVAL: variables._APP_MAINTENANCE_INTERVAL,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_EXECUTION: variables._APP_MAINTENANCE_RETENTION_EXECUTION,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_ABUSE: variables._APP_MAINTENANCE_RETENTION_ABUSE,
 | 
				
			||||||
 | 
										_APP_MAINTENANCE_RETENTION_AUDIT: variables._APP_MAINTENANCE_RETENTION_AUDIT
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteUsage: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
 | 
				
			||||||
 | 
										_APP_DB_HOST: variables._APP_DB_HOST,
 | 
				
			||||||
 | 
										_APP_DB_PORT: variables._APP_DB_PORT,
 | 
				
			||||||
 | 
										_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										_APP_DB_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										_APP_DB_PASS: variables._APP_DB_PASS,
 | 
				
			||||||
 | 
										_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
 | 
				
			||||||
 | 
										_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT,
 | 
				
			||||||
 | 
										_APP_USAGE_AGGREGATION_INTERVAL: variables._APP_USAGE_AGGREGATION_INTERVAL,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								appwriteSchedule: {
 | 
				
			||||||
 | 
									image: `${image}:${version}`,
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_ENV: variables._APP_ENV,
 | 
				
			||||||
 | 
										_APP_REDIS_HOST: variables._APP_REDIS_HOST,
 | 
				
			||||||
 | 
										_APP_REDIS_PORT: variables._APP_REDIS_PORT,
 | 
				
			||||||
 | 
										_APP_REDIS_USER: variables._APP_REDIS_USER,
 | 
				
			||||||
 | 
										_APP_REDIS_PASS: variables._APP_REDIS_PASS
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								mariadb: {
 | 
				
			||||||
 | 
									image: 'mariadb:10.7',
 | 
				
			||||||
 | 
									volumes: [`${id}-appwrite-mariadb:/var/lib/mysql`],
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										MYSQL_ROOT_PASSWORD: variables._APP_DB_ROOT_PASS,
 | 
				
			||||||
 | 
										MYSQL_DATABASE: variables._APP_DB_SCHEMA,
 | 
				
			||||||
 | 
										MYSQL_USER: variables._APP_DB_USER,
 | 
				
			||||||
 | 
										MYSQL_PASSWORD: variables._APP_DB_PASS
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								redis: {
 | 
				
			||||||
 | 
									image: 'redis:6.0-alpine3.12',
 | 
				
			||||||
 | 
									volumes: [`${id}-appwrite-redis:/data`]
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								influxdb: {
 | 
				
			||||||
 | 
									image: 'appwrite/influxdb:1.0.0',
 | 
				
			||||||
 | 
									volumes: [`${id}-appwrite-influxdb:/var/lib/influxdb`]
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								telegraf: {
 | 
				
			||||||
 | 
									image: 'appwrite/telegraf:1.0.0',
 | 
				
			||||||
 | 
									environmentVariables: {
 | 
				
			||||||
 | 
										_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
 | 
				
			||||||
 | 
										_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const composeFile: ComposeFile = {
 | 
				
			||||||
 | 
								version: '3.8',
 | 
				
			||||||
 | 
								services: {
 | 
				
			||||||
 | 
									[id]: {
 | 
				
			||||||
 | 
										container_name: id,
 | 
				
			||||||
 | 
										image: config.image,
 | 
				
			||||||
 | 
										networks: [network],
 | 
				
			||||||
 | 
										volumes: [...config.appwrite.volumes],
 | 
				
			||||||
 | 
										environment: config.environmentVariables,
 | 
				
			||||||
 | 
										restart: 'always',
 | 
				
			||||||
 | 
										labels: makeLabelForServices('appwrite'),
 | 
				
			||||||
 | 
										...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
 | 
				
			||||||
 | 
										deploy: {
 | 
				
			||||||
 | 
											restart_policy: {
 | 
				
			||||||
 | 
												condition: 'on-failure',
 | 
				
			||||||
 | 
												delay: '5s',
 | 
				
			||||||
 | 
												max_attempts: 3,
 | 
				
			||||||
 | 
												window: '120s'
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								networks: {
 | 
				
			||||||
 | 
									[network]: {
 | 
				
			||||||
 | 
										external: true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								volumes: {
 | 
				
			||||||
 | 
									[config.volume.split(':')[0]]: {
 | 
				
			||||||
 | 
										name: config.volume.split(':')[0]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							const composeFileDestination = `${workdir}/docker-compose.yaml`;
 | 
				
			||||||
 | 
							await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
 | 
				
			||||||
 | 
								await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									status: 200
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								return ErrorHandler(error);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/routes/services/[id]/appwrite-wip/stop.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/routes/services/[id]/appwrite-wip/stop.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { getUserDetails, removeDestinationDocker } from '$lib/common';
 | 
				
			||||||
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import { checkContainer } from '$lib/haproxy';
 | 
				
			||||||
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const service = await db.getService({ id, teamId });
 | 
				
			||||||
 | 
							const { destinationDockerId, destinationDocker, fqdn } = service;
 | 
				
			||||||
 | 
							if (destinationDockerId) {
 | 
				
			||||||
 | 
								const engine = destinationDocker.engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									const found = await checkContainer(engine, id);
 | 
				
			||||||
 | 
									if (found) {
 | 
				
			||||||
 | 
										await removeDestinationDocker({ id, engine });
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (error) {
 | 
				
			||||||
 | 
									console.error(error);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								status: 200
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,19 +1,56 @@
 | 
				
			|||||||
import { asyncExecShell, getDomain, getEngine, getUserDetails } from '$lib/common';
 | 
					import { asyncExecShell, getDomain, getEngine, getUserDetails } from '$lib/common';
 | 
				
			||||||
import * as db from '$lib/database';
 | 
					import * as db from '$lib/database';
 | 
				
			||||||
import { ErrorHandler } from '$lib/database';
 | 
					import { ErrorHandler } from '$lib/database';
 | 
				
			||||||
 | 
					import { t } from '$lib/translations';
 | 
				
			||||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
					import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import getPort from 'get-port';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
	const { status, body } = await getUserDetails(event);
 | 
						const { status, body } = await getUserDetails(event);
 | 
				
			||||||
	if (status === 401) return { status, body };
 | 
						if (status === 401) return { status, body };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { id } = event.params;
 | 
						const { id } = event.params;
 | 
				
			||||||
	let { fqdn } = await event.request.json();
 | 
						let { fqdn, exposePort, otherFqdns } = await event.request.json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fqdn) fqdn = fqdn.toLowerCase();
 | 
						if (fqdn) fqdn = fqdn.toLowerCase();
 | 
				
			||||||
 | 
						if (otherFqdns) otherFqdns = otherFqdns.map((fqdn) => fqdn.toLowerCase());
 | 
				
			||||||
 | 
						if (exposePort) exposePort = Number(exposePort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const found = await db.isDomainConfigured({ id, fqdn });
 | 
							let found = await db.isDomainConfigured({ id, fqdn });
 | 
				
			||||||
 | 
							if (found) {
 | 
				
			||||||
 | 
								throw {
 | 
				
			||||||
 | 
									message: t.get('application.domain_already_in_use', {
 | 
				
			||||||
 | 
										domain: getDomain(fqdn).replace('www.', '')
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (otherFqdns) {
 | 
				
			||||||
 | 
								for (const ofqdn of otherFqdns) {
 | 
				
			||||||
 | 
									const domain = getDomain(ofqdn);
 | 
				
			||||||
 | 
									const nakedDomain = domain.replace('www.', '');
 | 
				
			||||||
 | 
									found = await db.isDomainConfigured({ id, fqdn: ofqdn, checkOwn: true });
 | 
				
			||||||
 | 
									if (found) {
 | 
				
			||||||
 | 
										throw {
 | 
				
			||||||
 | 
											message: t.get('application.domain_already_in_use', {
 | 
				
			||||||
 | 
												domain: nakedDomain
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (exposePort) {
 | 
				
			||||||
 | 
								exposePort = Number(exposePort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (exposePort < 1024 || exposePort > 65535) {
 | 
				
			||||||
 | 
									throw { message: `Exposed Port needs to be between 1024 and 65535.` };
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const publicPort = await getPort({ port: exposePort });
 | 
				
			||||||
 | 
								if (publicPort !== exposePort) {
 | 
				
			||||||
 | 
									throw { message: `Port ${exposePort} is already in use.` };
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			status: found ? 500 : 200,
 | 
								status: found ? 500 : 200,
 | 
				
			||||||
			body: {
 | 
								body: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import { ErrorHandler, getServiceImage } from '$lib/database';
 | 
				
			|||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
 | 
					import { makeLabelForServices } from '$lib/buildPacks/common';
 | 
				
			||||||
import type { ComposeFile } from '$lib/types/composeFile';
 | 
					import type { ComposeFile } from '$lib/types/composeFile';
 | 
				
			||||||
import type { Service, DestinationDocker, Prisma } from '@prisma/client';
 | 
					import type { Service, DestinationDocker, Prisma } from '@prisma/client';
 | 
				
			||||||
 | 
					import { getServiceMainPort } from '$lib/components/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
	const { teamId, status, body } = await getUserDetails(event);
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
@@ -30,6 +31,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
			destinationDockerId,
 | 
								destinationDockerId,
 | 
				
			||||||
			destinationDocker,
 | 
								destinationDocker,
 | 
				
			||||||
			serviceSecret,
 | 
								serviceSecret,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
			fider: {
 | 
								fider: {
 | 
				
			||||||
				postgresqlUser,
 | 
									postgresqlUser,
 | 
				
			||||||
				postgresqlPassword,
 | 
									postgresqlPassword,
 | 
				
			||||||
@@ -48,6 +50,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
		} = service;
 | 
							} = service;
 | 
				
			||||||
		const network = destinationDockerId && destinationDocker.network;
 | 
							const network = destinationDockerId && destinationDocker.network;
 | 
				
			||||||
		const host = getEngine(destinationDocker.engine);
 | 
							const host = getEngine(destinationDocker.engine);
 | 
				
			||||||
 | 
							const port = getServiceMainPort('fider');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const { workdir } = await createDirectories({ repository: type, buildId: id });
 | 
							const { workdir } = await createDirectories({ repository: type, buildId: id });
 | 
				
			||||||
		const image = getServiceImage(type);
 | 
							const image = getServiceImage(type);
 | 
				
			||||||
@@ -97,6 +100,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
					volumes: [],
 | 
										volumes: [],
 | 
				
			||||||
					restart: 'always',
 | 
										restart: 'always',
 | 
				
			||||||
					labels: makeLabelForServices('fider'),
 | 
										labels: makeLabelForServices('fider'),
 | 
				
			||||||
 | 
										...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
 | 
				
			||||||
					deploy: {
 | 
										deploy: {
 | 
				
			||||||
						restart_policy: {
 | 
											restart_policy: {
 | 
				
			||||||
							condition: 'on-failure',
 | 
												condition: 'on-failure',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,11 +11,12 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
	let {
 | 
						let {
 | 
				
			||||||
		name,
 | 
							name,
 | 
				
			||||||
		fqdn,
 | 
							fqdn,
 | 
				
			||||||
 | 
							exposePort,
 | 
				
			||||||
		ghost: { mariadbDatabase }
 | 
							ghost: { mariadbDatabase }
 | 
				
			||||||
	} = await event.request.json();
 | 
						} = await event.request.json();
 | 
				
			||||||
	if (fqdn) fqdn = fqdn.toLowerCase();
 | 
						if (fqdn) fqdn = fqdn.toLowerCase();
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		await db.updateGhostService({ id, fqdn, name, mariadbDatabase });
 | 
							await db.updateGhostService({ id, fqdn, name, exposePort, mariadbDatabase });
 | 
				
			||||||
		return { status: 201 };
 | 
							return { status: 201 };
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		return ErrorHandler(error);
 | 
							return ErrorHandler(error);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import type { RequestHandler } from '@sveltejs/kit';
 | 
				
			|||||||
import { ErrorHandler, getServiceImage } from '$lib/database';
 | 
					import { ErrorHandler, getServiceImage } from '$lib/database';
 | 
				
			||||||
import { makeLabelForServices } from '$lib/buildPacks/common';
 | 
					import { makeLabelForServices } from '$lib/buildPacks/common';
 | 
				
			||||||
import type { ComposeFile } from '$lib/types/composeFile';
 | 
					import type { ComposeFile } from '$lib/types/composeFile';
 | 
				
			||||||
 | 
					import { getServiceMainPort } from '$lib/components/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const post: RequestHandler = async (event) => {
 | 
					export const post: RequestHandler = async (event) => {
 | 
				
			||||||
	const { teamId, status, body } = await getUserDetails(event);
 | 
						const { teamId, status, body } = await getUserDetails(event);
 | 
				
			||||||
@@ -19,6 +20,8 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const { id } = event.params;
 | 
						const { id } = event.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const port = getServiceMainPort('ghost');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const service = await db.getService({ id, teamId });
 | 
							const service = await db.getService({ id, teamId });
 | 
				
			||||||
		const {
 | 
							const {
 | 
				
			||||||
@@ -27,6 +30,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
			destinationDockerId,
 | 
								destinationDockerId,
 | 
				
			||||||
			destinationDocker,
 | 
								destinationDocker,
 | 
				
			||||||
			serviceSecret,
 | 
								serviceSecret,
 | 
				
			||||||
 | 
								exposePort,
 | 
				
			||||||
			fqdn,
 | 
								fqdn,
 | 
				
			||||||
			ghost: {
 | 
								ghost: {
 | 
				
			||||||
				defaultEmail,
 | 
									defaultEmail,
 | 
				
			||||||
@@ -89,6 +93,7 @@ export const post: RequestHandler = async (event) => {
 | 
				
			|||||||
					volumes: [config.ghost.volume],
 | 
										volumes: [config.ghost.volume],
 | 
				
			||||||
					environment: config.ghost.environmentVariables,
 | 
										environment: config.ghost.environmentVariables,
 | 
				
			||||||
					restart: 'always',
 | 
										restart: 'always',
 | 
				
			||||||
 | 
										...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
 | 
				
			||||||
					labels: makeLabelForServices('ghost'),
 | 
										labels: makeLabelForServices('ghost'),
 | 
				
			||||||
					depends_on: [`${id}-mariadb`],
 | 
										depends_on: [`${id}-mariadb`],
 | 
				
			||||||
					deploy: {
 | 
										deploy: {
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user