mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-25 12:33:35 +00:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e940807ae | ||
|
|
b081743f54 | ||
|
|
34bb9f301f | ||
|
|
ed8a6daeea | ||
|
|
9e81ab43ac | ||
|
|
32d94cbe97 | ||
|
|
46a83aa457 | ||
|
|
08d7593ca9 | ||
|
|
a50f7a7cc2 | ||
|
|
2c33447f9f | ||
|
|
d67a3f51ec | ||
|
|
2719974262 | ||
|
|
eb5aebd58d | ||
|
|
98dbf3d8a5 | ||
|
|
d9489a2cb4 | ||
|
|
95832d34f7 | ||
|
|
d3e9aea63d | ||
|
|
d6972e2ed1 | ||
|
|
50844e98be | ||
|
|
5c6fcfebf9 | ||
|
|
84cfe6fb42 | ||
|
|
abf0aeb2a8 | ||
|
|
a7aca0ce8b | ||
|
|
67bb5d973b | ||
|
|
662948d622 | ||
|
|
f5bedfdf7f | ||
|
|
db9db61d92 | ||
|
|
d255cb1973 | ||
|
|
6529271de2 | ||
|
|
0dd32b5319 | ||
|
|
b032da798b | ||
|
|
a1a9f1531e | ||
|
|
f71b54deb2 | ||
|
|
c63430e342 | ||
|
|
6821b128ad | ||
|
|
3f8d44a01c | ||
|
|
3aef04437c | ||
|
|
53e32c038b | ||
|
|
1660510614 | ||
|
|
69f5601b3e | ||
|
|
6e22fecc98 | ||
|
|
d18b2b6a1f | ||
|
|
4b0370ac08 | ||
|
|
750ef80777 | ||
|
|
59c62923be | ||
|
|
68b220d06e | ||
|
|
250ea64203 | ||
|
|
0ab57396d2 | ||
|
|
1e36856e65 | ||
|
|
cfdc8db543 | ||
|
|
1f25bc411f | ||
|
|
972f77c790 | ||
|
|
795f99bb47 | ||
|
|
54f7142b2b | ||
|
|
26eacfc2c0 | ||
|
|
e2bf02841f | ||
|
|
6a59b8d27c | ||
|
|
7fc43ef2bb | ||
|
|
70a3fc247e | ||
|
|
56ab8312f1 | ||
|
|
6fb6a514ac | ||
|
|
b01f5f47b3 | ||
|
|
ebdd3601b3 | ||
|
|
c0d711170b | ||
|
|
01ea86479d | ||
|
|
eb62888c39 | ||
|
|
b006fe8f68 | ||
|
|
dc3add495c | ||
|
|
59086e9eb4 | ||
|
|
e563988596 | ||
|
|
5a206a140c | ||
|
|
dbf910ff38 | ||
|
|
35b31dce2b | ||
|
|
1ec620be4b | ||
|
|
8516ac671a | ||
|
|
3b7fdebe8c | ||
|
|
17ac3048ac | ||
|
|
4e43efef50 | ||
|
|
4f4f5b1c01 | ||
|
|
1fa5c5e021 | ||
|
|
436e0e3a2b | ||
|
|
e717c1d599 | ||
|
|
ae5d90eb47 | ||
|
|
c095cb58b3 | ||
|
|
6bba37c36d | ||
|
|
60a428a952 | ||
|
|
16b7c1708b | ||
|
|
3435f92fcb | ||
|
|
cef571b8cc | ||
|
|
242bc61e2d | ||
|
|
c917135bd3 | ||
|
|
3802158ad5 | ||
|
|
e452f68614 | ||
|
|
9586213dd1 | ||
|
|
30781f218c | ||
|
|
697c42ff66 | ||
|
|
37d8f1847c | ||
|
|
2af13fff55 | ||
|
|
51e8ca8de0 | ||
|
|
06228cd2a7 | ||
|
|
0033baafdc | ||
|
|
79dfc6a660 | ||
|
|
972b0fa811 | ||
|
|
ad51a9ebc8 | ||
|
|
51a40d049d | ||
|
|
8b3113bd92 | ||
|
|
d6b6938555 | ||
|
|
ce52608f19 | ||
|
|
ede37d296b | ||
|
|
6374b1284b | ||
|
|
6ac8dd8907 | ||
|
|
24c655d7ef | ||
|
|
1f087cc29a | ||
|
|
c3684a1650 | ||
|
|
a410fd0776 | ||
|
|
271fb1358d | ||
|
|
a4d53a28eb | ||
|
|
e69e32f6c7 | ||
|
|
650409dde3 | ||
|
|
f3f4bb5105 |
16
.devcontainer/Dockerfile
Normal file
16
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node/.devcontainer/base.Dockerfile
|
||||||
|
|
||||||
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
|
ARG VARIANT="16-bullseye"
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||||
|
|
||||||
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||||
|
|
||||||
|
# [Optional] Uncomment if you want to install an additional version of node using nvm
|
||||||
|
# ARG EXTRA_NODE_VERSION=10
|
||||||
|
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
|
||||||
|
|
||||||
|
# [Optional] Uncomment if you want to install more global node modules
|
||||||
|
RUN su node -c "npm install -g pnpm"
|
||||||
28
.devcontainer/devcontainer.json
Normal file
28
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node
|
||||||
|
{
|
||||||
|
"name": "Node.js",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
|
||||||
|
// Append -bullseye or -buster to pin to an OS version.
|
||||||
|
// Use -bullseye variants on local arm64/Apple Silicon.
|
||||||
|
"args": {
|
||||||
|
"VARIANT": "16-bullseye"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
"settings": {},
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": ["dbaeumer.vscode-eslint", "svelte.svelte-vscode"],
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
"forwardPorts": [3000],
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "cp .env.template .env && pnpm install && pnpm db:push && pnpm db:seed",
|
||||||
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
"remoteUser": "node",
|
||||||
|
"features": {
|
||||||
|
"docker-in-docker": "20.10",
|
||||||
|
"github-cli": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,3 +3,6 @@ contact_links:
|
|||||||
- name: 🤔 Questions and Help
|
- name: 🤔 Questions and Help
|
||||||
url: https://discord.com/invite/6rDM4fkymF
|
url: https://discord.com/invite/6rDM4fkymF
|
||||||
about: Reach out to us on discord or our github discussions page.
|
about: Reach out to us on discord or our github discussions page.
|
||||||
|
- name: 🙋♂️ service request
|
||||||
|
url: https://feedback.coolify.io/
|
||||||
|
about: want to request a new service? for e.g wordpress, hasura, appwrite etc...
|
||||||
|
|||||||
@@ -15,7 +15,13 @@ This is a little list of what you can do to help the project:
|
|||||||
|
|
||||||
## 👋 Introduction
|
## 👋 Introduction
|
||||||
|
|
||||||
🔴 At the moment, Coolify **doesn't support Windows**. You must use Linux or MacOS.
|
### Setup with github codespaces
|
||||||
|
|
||||||
|
If you have github codespaces enabled then you can just create a codespace and run `pnpm dev` to run your the dev environment. All the required dependencies and packages has been configured for you already.
|
||||||
|
|
||||||
|
### Setup locally in your machine
|
||||||
|
|
||||||
|
> 🔴 At the moment, Coolify **doesn't support Windows**. You must use Linux or MacOS. 💡 Although windows users can use github codespaces for development
|
||||||
|
|
||||||
#### Recommended Pull Request Guideline
|
#### Recommended Pull Request Guideline
|
||||||
|
|
||||||
@@ -35,20 +41,16 @@ Due to the lock file, this repository is best with [pnpm](https://pnpm.io). I re
|
|||||||
|
|
||||||
You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
||||||
|
|
||||||
#### Setup a local development environment
|
#### Steps for local setup
|
||||||
|
|
||||||
- Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
1. Copy `.env.template` to `.env` and set the `COOLIFY_APP_ID` environment variable to something cool.
|
||||||
- Install dependencies with `pnpm install`.
|
2. Install dependencies with `pnpm install`.
|
||||||
- Need to create a local SQlite database with `pnpm db:push`.
|
3. Need to create a local SQlite database with `pnpm db:push`.
|
||||||
- This will apply all migrations at `db/dev.db`.
|
|
||||||
- Seed the database with base entities with `pnpm db:seed`
|
|
||||||
- You can start coding after starting `pnpm dev`.
|
|
||||||
|
|
||||||
#### How to start after you set up your local fork?
|
This will apply all migrations at `db/dev.db`.
|
||||||
|
|
||||||
This repository works better with [pnpm](https://pnpm.io) due to the lock file. I recommend you to give it a try and use `pnpm` as well because it is cool and efficient!
|
4. Seed the database with base entities with `pnpm db:seed`
|
||||||
|
5. You can start coding after starting `pnpm dev`.
|
||||||
You need to have [Docker Engine](https://docs.docker.com/engine/install/) installed locally.
|
|
||||||
|
|
||||||
## 🧑💻 Developer contribution
|
## 🧑💻 Developer contribution
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ 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
|
## 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!
|
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
|
||||||
@@ -61,6 +62,8 @@ These are the predefined build packs, but with the Docker build pack, you can ho
|
|||||||
- Laravel
|
- Laravel
|
||||||
- Rust
|
- Rust
|
||||||
- Docker
|
- Docker
|
||||||
|
- Python
|
||||||
|
- Deno
|
||||||
|
|
||||||
### Databases
|
### Databases
|
||||||
|
|
||||||
@@ -77,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)
|
||||||
|
|||||||
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
|
||||||
|
|||||||
42
package.json
42
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.7.0",
|
"version": "2.9.6",
|
||||||
"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,61 +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",
|
"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"
|
||||||
},
|
},
|
||||||
|
|||||||
414
pnpm-lock.yaml
generated
414
pnpm-lock.yaml
generated
@@ -3,25 +3,26 @@ lockfileVersion: 5.4
|
|||||||
specifiers:
|
specifiers:
|
||||||
'@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
|
||||||
'@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
|
||||||
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
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
cross-var: 1.1.0
|
cross-var: 1.1.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
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
@@ -29,92 +30,95 @@ specifiers:
|
|||||||
eslint-plugin-svelte3: 3.4.1
|
eslint-plugin-svelte3: 3.4.1
|
||||||
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
|
||||||
husky: 7.0.4
|
husky: 7.0.4
|
||||||
is-ip: ^4.0.0
|
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
|
||||||
lint-staged: 12.4.0
|
lint-staged: 12.4.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
|
||||||
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-kit-cookie-session: 2.1.3
|
svelte-kit-cookie-session: 2.1.4
|
||||||
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
|
||||||
tailwindcss-scrollbar: 0.1.0
|
tailwindcss-scrollbar: 0.1.0
|
||||||
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
|
||||||
unique-names-generator: 4.7.1
|
unique-names-generator: 4.7.1
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iarna/toml': 2.2.5
|
'@iarna/toml': 2.2.5
|
||||||
'@prisma/client': 3.11.1_prisma@3.11.1
|
'@prisma/client': 3.11.1_prisma@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
|
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@3.0.24
|
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.24
|
||||||
unique-names-generator: 4.7.1
|
unique-names-generator: 4.7.1
|
||||||
|
|
||||||
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_svelte@3.47.0
|
'@sveltejs/adapter-static': 1.0.0-next.31
|
||||||
'@types/js-cookie': 3.0.1
|
'@sveltejs/kit': 1.0.0-next.334_svelte@3.48.0
|
||||||
|
'@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_r3ph5xlwsrsg4ewthrjemd3cfq
|
'@typescript-eslint/eslint-plugin': 4.31.1_lii63oz3usekbu5ehvrcuwn5jy
|
||||||
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
'@typescript-eslint/parser': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
|
||||||
'@zerodevx/svelte-toast': 0.7.1
|
'@zerodevx/svelte-toast': 0.7.1
|
||||||
autoprefixer: 10.4.4_postcss@8.4.12
|
autoprefixer: 10.4.7_postcss@8.4.13
|
||||||
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@7.32.0
|
eslint-config-prettier: 8.5.0_eslint@7.32.0
|
||||||
eslint-plugin-svelte3: 3.4.1_4oxeyilw5mxcaksmcxtpjddhfe
|
eslint-plugin-svelte3: 3.4.1_6wevxxng4y4ff26nzlndg2wnpa
|
||||||
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_sqtt6dzjlskmywoml5ykunxlce
|
prettier-plugin-svelte: 2.7.0_kkjbqzpydplecjtkxrgomroeru
|
||||||
prettier-plugin-tailwindcss: 0.1.10_prettier@2.6.2
|
prettier-plugin-tailwindcss: 0.1.11_prettier@2.6.2
|
||||||
prisma: 3.11.1
|
prisma: 3.11.1
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
svelte-check: 2.7.0_cp6olp7pwsfaq5mjijwt65d6uy
|
svelte-check: 2.7.1_f2ke6qjyzu5axsjd6yk3u4tn7a
|
||||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
svelte-preprocess: 4.10.6_nq4dx2skq5drra53vttuo4lltu
|
||||||
svelte-select: 4.4.7
|
svelte-select: 4.4.7
|
||||||
sveltekit-i18n: 2.1.2_svelte@3.47.0
|
sveltekit-i18n: 2.2.1_svelte@3.48.0
|
||||||
tailwindcss: 3.0.24_ts-node@10.7.0
|
tailwindcss: 3.0.24_ts-node@10.7.0
|
||||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
ts-node: 10.7.0_3smuweqyuzdazdnyhhezld6mfa
|
||||||
tslib: 2.3.1
|
tslib: 2.4.0
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
/@babel/code-frame/7.12.11:
|
/@babel/code-frame/7.12.11:
|
||||||
@@ -212,6 +216,31 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@jridgewell/resolve-uri/3.0.7:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
|
||||||
|
}
|
||||||
|
engines: { node: '>=6.0.0' }
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/sourcemap-codec/1.4.13:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
|
||||||
|
}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/trace-mapping/0.3.13:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
|
||||||
|
}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.0.7
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.13
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@nodelib/fs.scandir/2.1.5:
|
/@nodelib/fs.scandir/2.1.5:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -285,55 +314,55 @@ packages:
|
|||||||
picomatch: 2.3.0
|
picomatch: 2.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sentry/core/6.19.6:
|
/@sentry/core/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-biEotGRr44/vBCOegkTfC9rwqaqRKIpFljKGyYU6/NtzMRooktqOhjmjmItNCMRknArdeaQwA8lk2jcZDXX3Og==
|
integrity: sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/hub': 6.19.6
|
'@sentry/hub': 6.19.7
|
||||||
'@sentry/minimal': 6.19.6
|
'@sentry/minimal': 6.19.7
|
||||||
'@sentry/types': 6.19.6
|
'@sentry/types': 6.19.7
|
||||||
'@sentry/utils': 6.19.6
|
'@sentry/utils': 6.19.7
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sentry/hub/6.19.6:
|
/@sentry/hub/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-PuEOBZxvx3bjxcXmWWZfWXG+orojQiWzv9LQXjIgroVMKM/GG4QtZbnWl1hOckUj7WtKNl4hEGO2g/6PyCV/vA==
|
integrity: sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/types': 6.19.6
|
'@sentry/types': 6.19.7
|
||||||
'@sentry/utils': 6.19.6
|
'@sentry/utils': 6.19.7
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sentry/minimal/6.19.6:
|
/@sentry/minimal/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-T1NKcv+HTlmd8EbzUgnGPl4ySQGHWMCyZ8a8kXVMZOPDzphN3fVIzkYzWmSftCWp0rpabXPt9aRF2mfBKU+mAQ==
|
integrity: sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/hub': 6.19.6
|
'@sentry/hub': 6.19.7
|
||||||
'@sentry/types': 6.19.6
|
'@sentry/types': 6.19.7
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sentry/node/6.19.6:
|
/@sentry/node/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-kHQMfsy40ZxxdS9zMPmXCOOLWOJbQj6/aVSHt/L1QthYcgkAi7NJQNXnQIPWQDe8eP3DfNIWM7dc446coqjXrQ==
|
integrity: sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/core': 6.19.6
|
'@sentry/core': 6.19.7
|
||||||
'@sentry/hub': 6.19.6
|
'@sentry/hub': 6.19.7
|
||||||
'@sentry/types': 6.19.6
|
'@sentry/types': 6.19.7
|
||||||
'@sentry/utils': 6.19.6
|
'@sentry/utils': 6.19.7
|
||||||
cookie: 0.4.2
|
cookie: 0.4.2
|
||||||
https-proxy-agent: 5.0.0
|
https-proxy-agent: 5.0.0
|
||||||
lru_map: 0.3.3
|
lru_map: 0.3.3
|
||||||
@@ -342,22 +371,22 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sentry/types/6.19.6:
|
/@sentry/types/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-QH34LMJidEUPZK78l+Frt3AaVFJhEmIi05Zf8WHd9/iTt+OqvCHBgq49DDr1FWFqyYWm/QgW/3bIoikFpfsXyQ==
|
integrity: sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@sentry/utils/6.19.6:
|
/@sentry/utils/6.19.7:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-fAMWcsguL0632eWrROp/vhPgI7sBj/JROWVPzpabwVkm9z3m1rQm6iLFn4qfkZL8Ozy6NVZPXOQ7EXmeU24byg==
|
integrity: sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==
|
||||||
}
|
}
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/types': 6.19.6
|
'@sentry/types': 6.19.7
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -378,19 +407,29 @@ packages:
|
|||||||
tiny-glob: 0.2.9
|
tiny-glob: 0.2.9
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sveltejs/kit/1.0.0-next.316_svelte@3.47.0:
|
/@sveltejs/adapter-static/1.0.0-next.31:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-oLjWOWzjriJD2t210r7ALuH/8ZADrJGsOODzRCRSJvRBCt0Q7VKVLqwKbM/RlZzD1k8Af2uRodQT11kP98hAIA==
|
integrity: sha512-d9RNA/de5ljb+gN8mKA3YfmfJoTbYFdH96NYDD8u4Lu9O/ZnseUxOAcAmD4/LKbLXOY/oYhRpt029xT2owyI3Q==
|
||||||
}
|
}
|
||||||
engines: { node: '>=14.13' }
|
dependencies:
|
||||||
|
tiny-glob: 0.2.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@sveltejs/kit/1.0.0-next.334_svelte@3.48.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-HPMF1oYBfyOG6wfU0Y6F4SID8jphue9yF+PXJqVTDBL5Z2WBG2ogum6MavE8aWhq+g2H6w5y0jNT8+8DO2KTCA==
|
||||||
|
}
|
||||||
|
engines: { node: '>=16' }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3.44.0
|
svelte: ^3.44.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': 1.0.0-next.33_svelte@3.47.0+vite@2.9.1
|
'@sveltejs/vite-plugin-svelte': 1.0.0-next.33_svelte@3.48.0+vite@2.9.1
|
||||||
|
chokidar: 3.5.3
|
||||||
sade: 1.7.4
|
sade: 1.7.4
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
vite: 2.9.1
|
vite: 2.9.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- diff-match-patch
|
- diff-match-patch
|
||||||
@@ -400,7 +439,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sveltejs/vite-plugin-svelte/1.0.0-next.33_svelte@3.47.0+vite@2.9.1:
|
/@sveltejs/vite-plugin-svelte/1.0.0-next.33_svelte@3.48.0+vite@2.9.1:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-aj0h2+ZixgT+yoJFIs8dRRw/Cj9tgNu3+hY4CJikpa04mfhR61wXqJFfi2ZEFMUvFda5nCxKYIChFkc6wq5fJA==
|
integrity: sha512-aj0h2+ZixgT+yoJFIs8dRRw/Cj9tgNu3+hY4CJikpa04mfhR61wXqJFfi2ZEFMUvFda5nCxKYIChFkc6wq5fJA==
|
||||||
@@ -419,22 +458,22 @@ packages:
|
|||||||
kleur: 4.1.4
|
kleur: 4.1.4
|
||||||
magic-string: 0.25.7
|
magic-string: 0.25.7
|
||||||
require-relative: 0.8.7
|
require-relative: 0.8.7
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
svelte-hmr: 0.14.9_svelte@3.47.0
|
svelte-hmr: 0.14.9_svelte@3.48.0
|
||||||
vite: 2.9.1
|
vite: 2.9.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@sveltekit-i18n/base/1.1.1_svelte@3.47.0:
|
/@sveltekit-i18n/base/1.2.1_svelte@3.48.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-J/sMU0OwS3dCLOuilHMBqu8vZHuuXiNV9vFJx8Nb4/b5BlR/KCZ4bCXI8wZR02GHeCOYKZxWus07CM1scxa/jw==
|
integrity: sha512-F8gqG2+KAOeT0o2wYlUrW3TRCX7zaD7rBy/1CEVNw0irfw9TgFf/ODmhubkHHT3+6Zk+SMz8RNgeuffBfAMbJw==
|
||||||
}
|
}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3.x
|
svelte: ^3.x
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@sveltekit-i18n/parser-default': 1.0.3
|
'@sveltekit-i18n/parser-default': 1.0.3
|
||||||
dev: true
|
dev: true
|
||||||
@@ -492,7 +531,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/http-cache-semantics': 4.0.1
|
'@types/http-cache-semantics': 4.0.1
|
||||||
'@types/keyv': 3.1.3
|
'@types/keyv': 3.1.3
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
'@types/responselike': 1.0.0
|
'@types/responselike': 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -503,10 +542,10 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/js-cookie/3.0.1:
|
/@types/js-cookie/3.0.2:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-7wg/8gfHltklehP+oyJnZrz9XBuX5ZPP4zB6UsI84utdlkRYLnOm2HfpLXazTwZA+fpGn0ir8tGNgVnMEleBGQ==
|
integrity: sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==
|
||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -530,22 +569,22 @@ packages:
|
|||||||
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
|
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/node-forge/1.0.1:
|
/@types/node-forge/1.0.2:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==
|
integrity: sha512-J1OkeZGaW0y9Y7xD49Ja8O82B9l5nZDeoYuGWqIOYPAf9LR+xF23k9ILdzv8dH+2H033fx3D5oiA0GlmicI+sg==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/node/17.0.25:
|
/@types/node/17.0.34:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==
|
integrity: sha512-XImEz7XwTvDBtzlTnm8YvMqGW/ErMWBsKZ+hMTvnDIjGCKxwK5Xpc+c/oQjOauwq8M4OS11hEkpjX8rrI/eEgA==
|
||||||
}
|
}
|
||||||
|
|
||||||
/@types/pug/2.0.5:
|
/@types/pug/2.0.5:
|
||||||
@@ -561,7 +600,7 @@ packages:
|
|||||||
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/sass/1.16.1:
|
/@types/sass/1.16.1:
|
||||||
@@ -570,10 +609,10 @@ packages:
|
|||||||
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
|
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin/4.31.1_r3ph5xlwsrsg4ewthrjemd3cfq:
|
/@typescript-eslint/eslint-plugin/4.31.1_lii63oz3usekbu5ehvrcuwn5jy:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-UDqhWmd5i0TvPLmbK5xY3UZB0zEGseF+DHPghZ37Sb83Qd3p8ujhvAtkU4OF46Ka5Pm5kWvFIx0cCTBFKo0alA==
|
integrity: sha512-UDqhWmd5i0TvPLmbK5xY3UZB0zEGseF+DHPghZ37Sb83Qd3p8ujhvAtkU4OF46Ka5Pm5kWvFIx0cCTBFKo0alA==
|
||||||
@@ -587,21 +626,21 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/experimental-utils': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
'@typescript-eslint/experimental-utils': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
|
||||||
'@typescript-eslint/parser': 4.31.1_hrkuebk64jiu2ut2d2sm4oylnu
|
'@typescript-eslint/parser': 4.31.1_e4zyhrvfnqudwdx5bevnvkluy4
|
||||||
'@typescript-eslint/scope-manager': 4.31.1
|
'@typescript-eslint/scope-manager': 4.31.1
|
||||||
debug: 4.3.3
|
debug: 4.3.3
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
functional-red-black-tree: 1.0.1
|
functional-red-black-tree: 1.0.1
|
||||||
regexpp: 3.2.0
|
regexpp: 3.2.0
|
||||||
semver: 7.3.5
|
semver: 7.3.5
|
||||||
tsutils: 3.21.0_typescript@4.6.3
|
tsutils: 3.21.0_typescript@4.6.4
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/experimental-utils/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu:
|
/@typescript-eslint/experimental-utils/4.31.1_e4zyhrvfnqudwdx5bevnvkluy4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==
|
integrity: sha512-NtoPsqmcSsWty0mcL5nTZXMf7Ei0Xr2MT8jWjXMVgRK0/1qeQ2jZzLFUh4QtyJ4+/lPUyMw5cSfeeME+Zrtp9Q==
|
||||||
@@ -613,7 +652,7 @@ packages:
|
|||||||
'@types/json-schema': 7.0.9
|
'@types/json-schema': 7.0.9
|
||||||
'@typescript-eslint/scope-manager': 4.31.1
|
'@typescript-eslint/scope-manager': 4.31.1
|
||||||
'@typescript-eslint/types': 4.31.1
|
'@typescript-eslint/types': 4.31.1
|
||||||
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.3
|
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.4
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
eslint-utils: 3.0.0_eslint@7.32.0
|
eslint-utils: 3.0.0_eslint@7.32.0
|
||||||
@@ -622,7 +661,7 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/parser/4.31.1_hrkuebk64jiu2ut2d2sm4oylnu:
|
/@typescript-eslint/parser/4.31.1_e4zyhrvfnqudwdx5bevnvkluy4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-dnVZDB6FhpIby6yVbHkwTKkn2ypjVIfAR9nh+kYsA/ZL0JlTsd22BiDjouotisY3Irmd3OW1qlk9EI5R8GrvRQ==
|
integrity: sha512-dnVZDB6FhpIby6yVbHkwTKkn2ypjVIfAR9nh+kYsA/ZL0JlTsd22BiDjouotisY3Irmd3OW1qlk9EI5R8GrvRQ==
|
||||||
@@ -637,10 +676,10 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 4.31.1
|
'@typescript-eslint/scope-manager': 4.31.1
|
||||||
'@typescript-eslint/types': 4.31.1
|
'@typescript-eslint/types': 4.31.1
|
||||||
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.3
|
'@typescript-eslint/typescript-estree': 4.31.1_typescript@4.6.4
|
||||||
debug: 4.3.3
|
debug: 4.3.3
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
@@ -664,7 +703,7 @@ packages:
|
|||||||
engines: { node: ^8.10.0 || ^10.13.0 || >=11.10.1 }
|
engines: { node: ^8.10.0 || ^10.13.0 || >=11.10.1 }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/typescript-estree/4.31.1_typescript@4.6.3:
|
/@typescript-eslint/typescript-estree/4.31.1_typescript@4.6.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg==
|
integrity: sha512-EGHkbsUvjFrvRnusk6yFGqrqMBTue5E5ROnS5puj3laGQPasVUgwhrxfcgkdHNFECHAewpvELE1Gjv0XO3mdWg==
|
||||||
@@ -682,8 +721,8 @@ packages:
|
|||||||
globby: 11.0.4
|
globby: 11.0.4
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
semver: 7.3.5
|
semver: 7.3.5
|
||||||
tsutils: 3.21.0_typescript@4.6.3
|
tsutils: 3.21.0_typescript@4.6.4
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
@@ -956,22 +995,22 @@ packages:
|
|||||||
typpy: 2.3.11
|
typpy: 2.3.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/autoprefixer/10.4.4_postcss@8.4.12:
|
/autoprefixer/10.4.7_postcss@8.4.13:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==
|
integrity: sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==
|
||||||
}
|
}
|
||||||
engines: { node: ^10 || ^12 || >=14 }
|
engines: { node: ^10 || ^12 || >=14 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.20.2
|
browserslist: 4.20.3
|
||||||
caniuse-lite: 1.0.30001320
|
caniuse-lite: 1.0.30001338
|
||||||
fraction.js: 4.2.0
|
fraction.js: 4.2.0
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -1653,18 +1692,18 @@ packages:
|
|||||||
fill-range: 7.0.1
|
fill-range: 7.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/browserslist/4.20.2:
|
/browserslist/4.20.3:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
|
integrity: sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==
|
||||||
}
|
}
|
||||||
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001320
|
caniuse-lite: 1.0.30001338
|
||||||
electron-to-chromium: 1.4.93
|
electron-to-chromium: 1.4.137
|
||||||
escalade: 3.1.1
|
escalade: 3.1.1
|
||||||
node-releases: 2.0.2
|
node-releases: 2.0.4
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -1686,10 +1725,10 @@ packages:
|
|||||||
ieee754: 1.2.1
|
ieee754: 1.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/bullmq/1.80.4:
|
/bullmq/1.82.2:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-j3PyjU16gqmb3Md9QjMInJdbMvxIlSJx7mojtoP06LV9MfhzW75DkDrpSuJlF0H+0+u6MViV4hhaGTxky5OJWw==
|
integrity: sha512-pDmMl6HmL/7B41ldBK4lnmGUcobkI/n/a0T3d/volMWC0ULxsaZ6R6fDePk23LwH9Fxu4o9Ny+zurCL3vG7lbg==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
cron-parser: 4.2.1
|
cron-parser: 4.2.1
|
||||||
@@ -1753,10 +1792,10 @@ packages:
|
|||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001320:
|
/caniuse-lite/1.0.30001338:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==
|
integrity: sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==
|
||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -2048,10 +2087,10 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/dayjs/1.11.1:
|
/dayjs/1.11.2:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==
|
integrity: sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==
|
||||||
}
|
}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -2267,10 +2306,10 @@ packages:
|
|||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/electron-to-chromium/1.4.93:
|
/electron-to-chromium/1.4.137:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-ywq9Pc5Gwwpv7NG767CtoU8xF3aAUQJjH9//Wy3MBCg4w5JSLbJUq2L8IsCdzPMjvSgxuue9WcVaTOyyxCL0aQ==
|
integrity: sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==
|
||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -2615,7 +2654,7 @@ packages:
|
|||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-svelte3/3.4.1_4oxeyilw5mxcaksmcxtpjddhfe:
|
/eslint-plugin-svelte3/3.4.1_6wevxxng4y4ff26nzlndg2wnpa:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==
|
integrity: sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==
|
||||||
@@ -2626,7 +2665,7 @@ packages:
|
|||||||
svelte: ^3.2.0
|
svelte: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 7.32.0
|
eslint: 7.32.0
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-scope/5.1.1:
|
/eslint-scope/5.1.1:
|
||||||
@@ -3080,10 +3119,10 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/got/12.0.3:
|
/got/12.0.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-hmdcXi/S0gcAtDg4P8j/rM7+j3o1Aq6bXhjxkDhRY2ipe7PHpvx/14DgTY2czHOLaGeU8VRvRecidwfu9qdFug==
|
integrity: sha512-2Eyz4iU/ktq7wtMFXxzK7g5p35uNYLLdiZarZ5/Yn3IJlNEpBd5+dCgcAyxN8/8guZLszffwe3wVyw+DEVrpBg==
|
||||||
}
|
}
|
||||||
engines: { node: '>=14.16' }
|
engines: { node: '>=14.16' }
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3544,10 +3583,10 @@ packages:
|
|||||||
engines: { node: '>=10' }
|
engines: { node: '>=10' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/lint-staged/12.4.0:
|
/lint-staged/12.4.1:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-3X7MR0h9b7qf4iXf/1n7RlVAx+EzpAZXoCEMhVSpaBlgKDfH2ewf+QUm7BddFyq29v4dgPP+8+uYpWuSWx035A==
|
integrity: sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg==
|
||||||
}
|
}
|
||||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3884,10 +3923,10 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/nanoid/3.3.1:
|
/nanoid/3.3.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
|
integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||||
}
|
}
|
||||||
engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
|
engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3914,10 +3953,17 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/node-releases/2.0.2:
|
/node-os-utils/1.3.6:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==
|
integrity: sha512-WympE9ELtdOzNak/rAuuIV5DwvX/PTJtN0LjyWeGyTTR2Kt0sY56ldLoGbVBnfM1dz46VeO3sHcNZI5BZ+EB+w==
|
||||||
|
}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/node-releases/2.0.4:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
|
||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -4110,7 +4156,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/postcss-js/4.0.0_postcss@8.4.12:
|
/postcss-js/4.0.0_postcss@8.4.13:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
|
integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
|
||||||
@@ -4120,10 +4166,10 @@ packages:
|
|||||||
postcss: ^8.3.3
|
postcss: ^8.3.3
|
||||||
dependencies:
|
dependencies:
|
||||||
camelcase-css: 2.0.1
|
camelcase-css: 2.0.1
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/postcss-load-config/3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy:
|
/postcss-load-config/3.1.4_4jqnslpwnj4ifyjfqbkuebd4fy:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
||||||
@@ -4139,12 +4185,12 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
lilconfig: 2.0.5
|
lilconfig: 2.0.5
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
ts-node: 10.7.0_3z6inmgn4ud4moqealnfxgbl2m
|
ts-node: 10.7.0_3smuweqyuzdazdnyhhezld6mfa
|
||||||
yaml: 1.10.2
|
yaml: 1.10.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/postcss-nested/5.0.6_postcss@8.4.12:
|
/postcss-nested/5.0.6_postcss@8.4.13:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
|
integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
|
||||||
@@ -4153,7 +4199,7 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
postcss: ^8.2.14
|
postcss: ^8.2.14
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -4175,14 +4221,14 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/postcss/8.4.12:
|
/postcss/8.4.13:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
|
integrity: sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
|
||||||
}
|
}
|
||||||
engines: { node: ^10 || ^12 || >=14 }
|
engines: { node: ^10 || ^12 || >=14 }
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid: 3.3.1
|
nanoid: 3.3.4
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
@@ -4195,7 +4241,7 @@ packages:
|
|||||||
engines: { node: '>= 0.8.0' }
|
engines: { node: '>= 0.8.0' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/prettier-plugin-svelte/2.7.0_sqtt6dzjlskmywoml5ykunxlce:
|
/prettier-plugin-svelte/2.7.0_kkjbqzpydplecjtkxrgomroeru:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
|
integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==
|
||||||
@@ -4205,13 +4251,13 @@ packages:
|
|||||||
svelte: ^3.2.0
|
svelte: ^3.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/prettier-plugin-tailwindcss/0.1.10_prettier@2.6.2:
|
/prettier-plugin-tailwindcss/0.1.11_prettier@2.6.2:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-ooDGNuXUjgCXfShliVYQ6+0iXqUFXn+zdNInPe0WZN9qINt9srbLGFGY5jeVL4MXtY20/4S8JaBcd8l6N6NfCQ==
|
integrity: sha512-a28+1jvpIZQdZ/W97wOXb6VqI762MKE/TxMMuibMEHhyYsSxQA8Ek30KObd5kJI2HF1ldtSYprFayXJXi3pz8Q==
|
||||||
}
|
}
|
||||||
engines: { node: '>=12.17.0' }
|
engines: { node: '>=12.17.0' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -4513,7 +4559,7 @@ packages:
|
|||||||
integrity: sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==
|
integrity: sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w==
|
||||||
}
|
}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.3.1
|
tslib: 2.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/sade/1.7.4:
|
/sade/1.7.4:
|
||||||
@@ -4698,14 +4744,6 @@ packages:
|
|||||||
engines: { node: '>=0.10.0' }
|
engines: { node: '>=0.10.0' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/source-map/0.7.3:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
|
||||||
}
|
|
||||||
engines: { node: '>= 8' }
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/sourcemap-codec/1.4.8:
|
/sourcemap-codec/1.4.8:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
@@ -4878,24 +4916,24 @@ packages:
|
|||||||
engines: { node: '>= 0.4' }
|
engines: { node: '>= 0.4' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-check/2.7.0_cp6olp7pwsfaq5mjijwt65d6uy:
|
/svelte-check/2.7.1_f2ke6qjyzu5axsjd6yk3u4tn7a:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-GrvG24j0+i8AOm0k0KyJ6Dqc+TAR2yzB7rtS4nljHStunVxCTr/1KYlv4EsOeoqtHLzeWMOd5D2O6nDdP/yw4A==
|
integrity: sha512-vHVu2+SQ6ibt77iTQaq2oiOjBgGL48qqcg0ZdEOsP5pPOjgeyR9QbnaEdzdBs9nsVYBc/42haKtzb2uFqS8GVw==
|
||||||
}
|
}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3.24.0
|
svelte: ^3.24.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.13
|
||||||
chokidar: 3.5.3
|
chokidar: 3.5.3
|
||||||
fast-glob: 3.2.11
|
fast-glob: 3.2.11
|
||||||
import-fresh: 3.3.0
|
import-fresh: 3.3.0
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
sade: 1.7.4
|
sade: 1.7.4
|
||||||
source-map: 0.7.3
|
svelte: 3.48.0
|
||||||
svelte: 3.47.0
|
svelte-preprocess: 4.10.6_nq4dx2skq5drra53vttuo4lltu
|
||||||
svelte-preprocess: 4.10.6_igaqrb5onrwvsmrrc32h4m72ha
|
typescript: 4.6.4
|
||||||
typescript: 4.6.3
|
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- coffeescript
|
- coffeescript
|
||||||
@@ -4909,7 +4947,7 @@ packages:
|
|||||||
- sugarss
|
- sugarss
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-hmr/0.14.9_svelte@3.47.0:
|
/svelte-hmr/0.14.9_svelte@3.48.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==
|
integrity: sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==
|
||||||
@@ -4917,17 +4955,17 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: '>=3.19.0'
|
svelte: '>=3.19.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-kit-cookie-session/2.1.3:
|
/svelte-kit-cookie-session/2.1.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-7Xk3CNbpLAi1KodlsV5W5jULQ2NxQunaXtAYqAuzIEXIq2EwC4oDa25kdmHjNe33epV0t4r0WwxZOuSdJPsapg==
|
integrity: sha512-z/ckxHWguYyy66UqfId4Lu+A77ft/3mV5oozbRTI9bnQY0tyJfns/SM0ikdkj7OV5GiI8kq7GSOGvajWwyGeZw==
|
||||||
}
|
}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/svelte-preprocess/4.10.6_igaqrb5onrwvsmrrc32h4m72ha:
|
/svelte-preprocess/4.10.6_nq4dx2skq5drra53vttuo4lltu:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==
|
integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==
|
||||||
@@ -4975,11 +5013,11 @@ packages:
|
|||||||
'@types/sass': 1.16.1
|
'@types/sass': 1.16.1
|
||||||
detect-indent: 6.1.0
|
detect-indent: 6.1.0
|
||||||
magic-string: 0.25.7
|
magic-string: 0.25.7
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
sorcery: 0.10.0
|
sorcery: 0.10.0
|
||||||
strip-indent: 3.0.0
|
strip-indent: 3.0.0
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte-select/4.4.7:
|
/svelte-select/4.4.7:
|
||||||
@@ -4989,25 +5027,25 @@ packages:
|
|||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/svelte/3.47.0:
|
/svelte/3.48.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-4JaJp3HEoTCGARRWZQIZDUanhYv0iyoHikklVHVLH9xFE9db22g4TDv7CPeNA8HD1JgjXI1vlhR1JZvvhaTu2Q==
|
integrity: sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==
|
||||||
}
|
}
|
||||||
engines: { node: '>= 8' }
|
engines: { node: '>= 8' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/sveltekit-i18n/2.1.2_svelte@3.47.0:
|
/sveltekit-i18n/2.2.1_svelte@3.48.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-s5YxcbNd2EWNZaZR1A4Drt8s53E4fpUkN4XIWd3VRpw1pihZVWssqmBW1qkjQ6AB0kiu1Qwule+vt1HkbQOjrg==
|
integrity: sha512-1CyaRN6dBvp467JjBdji+nJf+7pZ3myFu+2YaCuGSAt09Cvt5ndfRbzy+aAd5WJdk6Lu/hnPEE7ZZFauTbDRNw==
|
||||||
}
|
}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3.x
|
svelte: ^3.x
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltekit-i18n/base': 1.1.1_svelte@3.47.0
|
'@sveltekit-i18n/base': 1.2.1_svelte@3.48.0
|
||||||
'@sveltekit-i18n/parser-default': 1.0.3
|
'@sveltekit-i18n/parser-default': 1.0.3
|
||||||
svelte: 3.47.0
|
svelte: 3.48.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/table/6.7.2:
|
/table/6.7.2:
|
||||||
@@ -5057,10 +5095,10 @@ packages:
|
|||||||
normalize-path: 3.0.0
|
normalize-path: 3.0.0
|
||||||
object-hash: 3.0.0
|
object-hash: 3.0.0
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
postcss-js: 4.0.0_postcss@8.4.12
|
postcss-js: 4.0.0_postcss@8.4.13
|
||||||
postcss-load-config: 3.1.4_ysmyu6g5dtd6yanj6zrab4uqoy
|
postcss-load-config: 3.1.4_4jqnslpwnj4ifyjfqbkuebd4fy
|
||||||
postcss-nested: 5.0.6_postcss@8.4.12
|
postcss-nested: 5.0.6_postcss@8.4.13
|
||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
postcss-value-parser: 4.2.0
|
postcss-value-parser: 4.2.0
|
||||||
quick-lru: 5.1.1
|
quick-lru: 5.1.1
|
||||||
@@ -5133,7 +5171,7 @@ packages:
|
|||||||
engines: { node: '>=0.10.0' }
|
engines: { node: '>=0.10.0' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/ts-node/10.7.0_3z6inmgn4ud4moqealnfxgbl2m:
|
/ts-node/10.7.0_3smuweqyuzdazdnyhhezld6mfa:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
|
integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
|
||||||
@@ -5155,14 +5193,14 @@ packages:
|
|||||||
'@tsconfig/node12': 1.0.9
|
'@tsconfig/node12': 1.0.9
|
||||||
'@tsconfig/node14': 1.0.1
|
'@tsconfig/node14': 1.0.1
|
||||||
'@tsconfig/node16': 1.0.2
|
'@tsconfig/node16': 1.0.2
|
||||||
'@types/node': 17.0.25
|
'@types/node': 17.0.34
|
||||||
acorn: 8.5.0
|
acorn: 8.5.0
|
||||||
acorn-walk: 8.2.0
|
acorn-walk: 8.2.0
|
||||||
arg: 4.1.3
|
arg: 4.1.3
|
||||||
create-require: 1.1.1
|
create-require: 1.1.1
|
||||||
diff: 4.0.2
|
diff: 4.0.2
|
||||||
make-error: 1.3.6
|
make-error: 1.3.6
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
v8-compile-cache-lib: 3.0.0
|
v8-compile-cache-lib: 3.0.0
|
||||||
yn: 3.1.1
|
yn: 3.1.1
|
||||||
dev: true
|
dev: true
|
||||||
@@ -5173,14 +5211,14 @@ packages:
|
|||||||
integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
}
|
}
|
||||||
|
|
||||||
/tslib/2.3.1:
|
/tslib/2.4.0:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||||
}
|
}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tsutils/3.21.0_typescript@4.6.3:
|
/tsutils/3.21.0_typescript@4.6.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
|
integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
|
||||||
@@ -5190,7 +5228,7 @@ packages:
|
|||||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
typescript: 4.6.3
|
typescript: 4.6.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tweetnacl/0.14.5:
|
/tweetnacl/0.14.5:
|
||||||
@@ -5223,10 +5261,10 @@ packages:
|
|||||||
engines: { node: '>=10' }
|
engines: { node: '>=10' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/typescript/4.6.3:
|
/typescript/4.6.4:
|
||||||
resolution:
|
resolution:
|
||||||
{
|
{
|
||||||
integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
|
integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
|
||||||
}
|
}
|
||||||
engines: { node: '>=4.2.0' }
|
engines: { node: '>=4.2.0' }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -5303,7 +5341,7 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.14.34
|
esbuild: 0.14.34
|
||||||
postcss: 8.4.12
|
postcss: 8.4.13
|
||||||
resolve: 1.22.0
|
resolve: 1.22.0
|
||||||
rollup: 2.61.1
|
rollup: 2.61.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -322,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())
|
||||||
@@ -333,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())
|
||||||
@@ -352,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
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ const prisma = new PrismaClient();
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const generator = require('generate-password');
|
const generator = require('generate-password');
|
||||||
const cuid = require('cuid');
|
const cuid = require('cuid');
|
||||||
|
const compare = require('compare-versions');
|
||||||
|
const { version } = require('../package.json');
|
||||||
|
const child = require('child_process');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
const algorithm = 'aes-256-ctr';
|
||||||
|
|
||||||
function generatePassword(length = 24) {
|
function generatePassword(length = 24) {
|
||||||
return generator.generate({
|
return generator.generate({
|
||||||
@@ -13,7 +19,7 @@ function generatePassword(length = 24) {
|
|||||||
strict: true
|
strict: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const algorithm = 'aes-256-ctr';
|
const asyncExecShell = util.promisify(child.exec);
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Enable registration for the first user
|
// Enable registration for the first user
|
||||||
@@ -64,6 +70,56 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (settings.isTraefikUsed) {
|
||||||
|
// Force stop Coolify Proxy, as it had a bug in < 2.9.2. TrustProxy + api.insecure
|
||||||
|
try {
|
||||||
|
const { stdout } = await asyncExecShell(
|
||||||
|
`docker inspect coolify-proxy --format '{{json .Config.Cmd}}'`
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!stdout
|
||||||
|
.replaceAll('[', '')
|
||||||
|
.replaceAll(']', '')
|
||||||
|
.replaceAll('"', '')
|
||||||
|
.replace('\n', '')
|
||||||
|
.split(',')
|
||||||
|
.includes('--entrypoints.web.forwardedHeaders.insecure=true')
|
||||||
|
) {
|
||||||
|
console.log('Reconfiguring Coolify Proxy (Traefik)...');
|
||||||
|
await asyncExecShell(`docker stop -t 0 coolify-proxy && docker rm coolify-proxy`);
|
||||||
|
const { stdout: Config } = await asyncExecShell(
|
||||||
|
`docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
||||||
|
);
|
||||||
|
const ip = JSON.parse(Config)[0].Gateway;
|
||||||
|
await asyncExecShell(
|
||||||
|
`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" \
|
||||||
|
--name coolify-proxy \
|
||||||
|
-d traefik:v2.6 \
|
||||||
|
--entrypoints.web.address=:80 \
|
||||||
|
--entrypoints.web.forwardedHeaders.insecure=true \
|
||||||
|
--entrypoints.websecure.address=:443 \
|
||||||
|
--entrypoints.websecure.forwardedHeaders.insecure=true \
|
||||||
|
--providers.docker=true \
|
||||||
|
--providers.docker.exposedbydefault=false \
|
||||||
|
--providers.http.endpoint=http://coolify:3000/webhooks/traefik/main.json \
|
||||||
|
--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`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
main()
|
main()
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -405,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,
|
||||||
@@ -425,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';
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
|||||||
export default async function (data) {
|
export default async function (data) {
|
||||||
try {
|
try {
|
||||||
const { baseImage, baseBuildImage } = data;
|
const { baseImage, baseBuildImage } = data;
|
||||||
await buildCacheImageWithNode(data, baseImage);
|
await buildCacheImageWithNode(data, baseBuildImage);
|
||||||
await createDockerfile(data, baseBuildImage);
|
await createDockerfile(data, baseImage);
|
||||||
await buildImage(data);
|
await buildImage(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
Dockerfile.push('WORKDIR /app');
|
Dockerfile.push('WORKDIR /app');
|
||||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||||
Dockerfile.push(`RUN ${installCommand}`);
|
Dockerfile.push(`RUN ${installCommand}`);
|
||||||
|
|||||||
@@ -96,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 = {
|
||||||
|
|||||||
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>
|
||||||
@@ -219,6 +219,18 @@ 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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,88 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
class={isAbsolute ? 'absolute top-0 left-0 -m-10 h-20 w-20' : 'mx-auto w-8 h-8'}
|
viewBox="0 0 128 128"
|
||||||
id="Layer_1"
|
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||||
data-name="Layer 1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 361.67651 499.33603"
|
|
||||||
><path
|
|
||||||
d="M203.77731,148.85754c-10.8147-12.762-20.13269-25.8139-22.02478-28.49224a.426.426,0,0,0-.70032.00006c-1.89172,2.6784-11.20758,15.73022-22.02191,28.49218-92.69141,118.085,14.62982,197.75507,14.62982,197.75507l.87.60461c.8136,12.32624,2.83508,30.041,2.83508,30.041H185.442s2.01282-17.63849,2.83-29.96106l.87549-.68451S296.46774,266.94257,203.77731,148.85754ZM181.404,344.88123h-.001s-4.811-4.10383-6.10962-6.16l-.01172-.22131,5.81946-128.56055a.30281.30281,0,0,1,.605,0l5.81946,128.56036-.01135.22065C186.21652,340.77625,181.404,344.88123,181.404,344.88123Z"
|
|
||||||
fill="#00684a"
|
|
||||||
/></svg
|
|
||||||
>
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill="#439934"
|
||||||
|
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"
|
||||||
|
/><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
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
|
||||||
|
fill-rule="evenodd"
|
||||||
|
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>
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -305,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 });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,16 +67,10 @@ export async function getSource({
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
export async function addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl, organization }) {
|
export async function addGitHubSource({ id, teamId, type, name, htmlUrl, apiUrl, organization }) {
|
||||||
await prisma.gitSource.update({
|
return await prisma.gitSource.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { type, name, htmlUrl, apiUrl, organization }
|
data: { type, name, htmlUrl, apiUrl, organization }
|
||||||
});
|
});
|
||||||
return await prisma.githubApp.create({
|
|
||||||
data: {
|
|
||||||
teams: { connect: { id: teamId } },
|
|
||||||
gitSource: { connect: { id } }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
export async function addGitLabSource({
|
export async function addGitLabSource({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const include: Prisma.ServiceInclude = {
|
|||||||
hasura: true,
|
hasura: true,
|
||||||
fider: true
|
fider: true
|
||||||
};
|
};
|
||||||
export async function listServicesWithIncludes() {
|
export async function listServicesWithIncludes(): Promise<Service[]> {
|
||||||
return await prisma.service.findMany({
|
return await prisma.service.findMany({
|
||||||
include,
|
include,
|
||||||
orderBy: { createdAt: 'desc' }
|
orderBy: { createdAt: 'desc' }
|
||||||
@@ -329,7 +329,8 @@ export async function updatePlausibleAnalyticsService({
|
|||||||
email,
|
email,
|
||||||
exposePort,
|
exposePort,
|
||||||
username,
|
username,
|
||||||
name
|
name,
|
||||||
|
scriptName
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
fqdn: string;
|
fqdn: string;
|
||||||
@@ -337,8 +338,12 @@ export async function updatePlausibleAnalyticsService({
|
|||||||
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({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { email, username, scriptName }
|
||||||
|
});
|
||||||
await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } });
|
await prisma.service.update({ where: { id }, data: { name, fqdn, exposePort } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +360,24 @@ export async function updateService({
|
|||||||
}): Promise<Service> {
|
}): Promise<Service> {
|
||||||
return await prisma.service.update({ where: { id }, data: { fqdn, name, exposePort } });
|
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,
|
||||||
@@ -414,22 +436,46 @@ export async function updateWordpress({
|
|||||||
name,
|
name,
|
||||||
exposePort,
|
exposePort,
|
||||||
mysqlDatabase,
|
mysqlDatabase,
|
||||||
extraConfig
|
extraConfig,
|
||||||
|
mysqlHost,
|
||||||
|
mysqlPort,
|
||||||
|
mysqlUser,
|
||||||
|
mysqlPassword
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
fqdn: string;
|
fqdn: string;
|
||||||
name: string;
|
name: string;
|
||||||
exposePort?: number;
|
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, exposePort, 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
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPnpm) {
|
if (isPnpm) {
|
||||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm');
|
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||||
Dockerfile.push('RUN pnpm add -g pnpm');
|
|
||||||
}
|
}
|
||||||
if (installCommand) {
|
if (installCommand) {
|
||||||
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
|
Dockerfile.push(`COPY .${baseDirectory || ''}/package.json ./`);
|
||||||
|
|||||||
@@ -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,77 @@ 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}`,
|
||||||
|
`--entryPoints.tcp.forwardedHeaders.insecure=true`,
|
||||||
|
`--providers.http.endpoint=${otherTraefikEndpoint}?id=${id}&privatePort=${privatePort}&publicPort=${publicPort}&type=tcp&address=${dependentId}`,
|
||||||
|
'--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 +211,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 +218,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 +231,76 @@ 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}`,
|
||||||
|
`--entryPoints.http.forwardedHeaders.insecure=true`,
|
||||||
|
`--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 +347,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" \
|
||||||
|
--name coolify-proxy \
|
||||||
|
-d ${defaultTraefikImage} \
|
||||||
|
--entrypoints.web.address=:80 \
|
||||||
|
--entrypoints.web.forwardedHeaders.insecure=true \
|
||||||
|
--entrypoints.websecure.address=:443 \
|
||||||
|
--entrypoints.websecure.forwardedHeaders.insecure=true \
|
||||||
|
--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 +435,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 +468,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 +502,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`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,8 +1,12 @@
|
|||||||
import { renewSSLCerts } from '$lib/letsencrypt';
|
import { renewSSLCerts } from '$lib/letsencrypt';
|
||||||
|
import { prisma } from '$lib/database';
|
||||||
|
|
||||||
export default async function (): Promise<void> {
|
export default async function (): Promise<void> {
|
||||||
try {
|
try {
|
||||||
return await renewSSLCerts();
|
const settings = await prisma.setting.findFirst();
|
||||||
|
if (!settings.isTraefikUsed) {
|
||||||
|
return await renewSSLCerts();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
throw 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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
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')}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
exposePort = Number(exposePort);
|
exposePort = Number(exposePort);
|
||||||
|
|
||||||
if (exposePort < 1024 || exposePort > 65535) {
|
if (exposePort < 1024 || exposePort > 65535) {
|
||||||
throw { message: `Expose Port needs to be between 1024 and 65535.` };
|
throw { message: `Exposed Port needs to be between 1024 and 65535.` };
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicPort = await getPort({ port: exposePort });
|
const publicPort = await getPort({ port: exposePort });
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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 { setDefaultConfiguration } from '$lib/buildPacks/common';
|
import { setDefaultConfiguration } from '$lib/buildPacks/common';
|
||||||
|
|
||||||
@@ -12,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,
|
||||||
|
|||||||
@@ -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,10 +35,9 @@
|
|||||||
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';
|
||||||
@@ -50,13 +48,22 @@
|
|||||||
import { get, 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;
|
||||||
@@ -67,6 +74,7 @@
|
|||||||
let isNonWWWDomainOK = false;
|
let isNonWWWDomainOK = false;
|
||||||
let isWWWDomainOK = false;
|
let isWWWDomainOK = false;
|
||||||
|
|
||||||
|
$: isDisabled = !$session.isAdmin || $status.application.isRunning;
|
||||||
let wsgis = [
|
let wsgis = [
|
||||||
{
|
{
|
||||||
value: 'None',
|
value: 'None',
|
||||||
@@ -78,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 handleSubmit();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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) {
|
||||||
@@ -129,6 +151,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
|
if (loading) return;
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
@@ -261,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">
|
||||||
@@ -354,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"
|
||||||
@@ -384,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}
|
||||||
@@ -420,8 +472,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
readonly={!$session.isAdmin || isRunning}
|
readonly={isDisabled}
|
||||||
disabled={!$session.isAdmin || isRunning}
|
disabled={isDisabled}
|
||||||
bind:this={domainEl}
|
bind:this={domainEl}
|
||||||
name="fqdn"
|
name="fqdn"
|
||||||
id="fqdn"
|
id="fqdn"
|
||||||
@@ -468,12 +520,12 @@
|
|||||||
<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'}
|
||||||
@@ -525,7 +577,8 @@
|
|||||||
<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>
|
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||||
<input
|
<input
|
||||||
readonly={!$session.isAdmin}
|
readonly={!$session.isAdmin && !$status.application.isRunning}
|
||||||
|
disabled={isDisabled}
|
||||||
name="exposePort"
|
name="exposePort"
|
||||||
id="exposePort"
|
id="exposePort"
|
||||||
bind:value={application.exposePort}
|
bind:value={application.exposePort}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => {
|
|||||||
const database = await db.getDatabase({ id, teamId });
|
const database = await db.getDatabase({ id, teamId });
|
||||||
if (database.destinationDockerId) {
|
if (database.destinationDockerId) {
|
||||||
const everStarted = await stopDatabase(database);
|
const everStarted = await stopDatabase(database);
|
||||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
}
|
}
|
||||||
await db.removeDatabase({ id });
|
await db.removeDatabase({ id });
|
||||||
return { status: 200 };
|
return { status: 200 };
|
||||||
|
|||||||
@@ -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,10 +22,14 @@ 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(id, destinationDocker, oldPublicPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
try {
|
try {
|
||||||
const database = await db.getDatabase({ id, teamId });
|
const database = await db.getDatabase({ id, teamId });
|
||||||
const everStarted = await stopDatabase(database);
|
const everStarted = await stopDatabase(database);
|
||||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
await db.setDatabase({ id, isPublic: false });
|
await db.setDatabase({ id, isPublic: false });
|
||||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
status: 200
|
status: 200
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -47,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>
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -27,12 +31,16 @@
|
|||||||
let loading = false;
|
let loading = false;
|
||||||
let loadingVerification = false;
|
let loadingVerification = false;
|
||||||
let dualCerts = service.dualCerts;
|
let dualCerts = service.dualCerts;
|
||||||
let showExposePort = service.exposePort !== null;
|
|
||||||
|
|
||||||
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 ? [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 }) {
|
||||||
@@ -63,6 +71,28 @@
|
|||||||
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`;
|
||||||
|
if (service.type === 'wordpress') {
|
||||||
|
service.wordpress.mysqlDatabase = 'db';
|
||||||
|
}
|
||||||
|
if (service.type === 'plausibleanalytics') {
|
||||||
|
service.plausibleAnalytics.email = 'noreply@demo.com';
|
||||||
|
service.plausibleAnalytics.username = 'admin';
|
||||||
|
}
|
||||||
|
if (service.type === 'minio') {
|
||||||
|
service.minio.apiFqdn = `http://${cuid()}.demo.coolify.io`;
|
||||||
|
}
|
||||||
|
if (service.type === 'ghost') {
|
||||||
|
service.ghost.mariadbDatabase = 'db';
|
||||||
|
}
|
||||||
|
if (service.type === 'fider') {
|
||||||
|
service.fider.emailNoreply = 'noreply@demo.com';
|
||||||
|
}
|
||||||
|
await handleSubmit();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mx-auto max-w-4xl px-6 pb-12">
|
<div class="mx-auto max-w-4xl px-6 pb-12">
|
||||||
@@ -87,6 +117,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>
|
||||||
@@ -132,25 +170,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}
|
||||||
@@ -161,34 +236,23 @@
|
|||||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<Setting
|
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||||
isCenter={false}
|
<input
|
||||||
bind:setting={showExposePort}
|
readonly={!$session.isAdmin && !isRunning}
|
||||||
on:click={() => {
|
disabled={!$session.isAdmin || isRunning}
|
||||||
showExposePort = !showExposePort;
|
name="exposePort"
|
||||||
service.exposePort = undefined;
|
id="exposePort"
|
||||||
}}
|
bind:value={service.exposePort}
|
||||||
title={$t('application.expose_a_port')}
|
placeholder="12345"
|
||||||
description="Expose a port to the host system"
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
|
|
||||||
{#if showExposePort}
|
|
||||||
<div class="grid grid-cols-2 items-center">
|
|
||||||
<label for="exposePort" class="text-base font-bold text-stone-100">Expose Port</label>
|
|
||||||
<input
|
|
||||||
readonly={!$session.isAdmin}
|
|
||||||
name="exposePort"
|
|
||||||
id="exposePort"
|
|
||||||
bind:value={service.exposePort}
|
|
||||||
placeholder="12345"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#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.length > 0) otherFqdns = otherFqdns.map((f) => f.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 && otherFqdns.length > 0) {
|
||||||
|
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);
|
||||||
@@ -56,7 +59,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
fider: {
|
fider: {
|
||||||
image: `${image}:${version}`,
|
image: `${image}:${version}`,
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
HOST_DOMAIN: domain,
|
BASE_URL: domain,
|
||||||
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
|
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
|
||||||
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
|
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
|
||||||
EMAIL_NOREPLY: emailNoreply,
|
EMAIL_NOREPLY: emailNoreply,
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -7,6 +7,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);
|
||||||
@@ -23,10 +24,12 @@ export const post: RequestHandler = async (event) => {
|
|||||||
destinationDockerId,
|
destinationDockerId,
|
||||||
destinationDocker,
|
destinationDocker,
|
||||||
serviceSecret,
|
serviceSecret,
|
||||||
|
exposePort,
|
||||||
hasura: { postgresqlUser, postgresqlPassword, postgresqlDatabase }
|
hasura: { postgresqlUser, postgresqlPassword, postgresqlDatabase }
|
||||||
} = 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('hasura');
|
||||||
|
|
||||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
const image = getServiceImage(type);
|
const image = getServiceImage(type);
|
||||||
@@ -65,6 +68,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
volumes: [],
|
volumes: [],
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
labels: makeLabelForServices('hasura'),
|
labels: makeLabelForServices('hasura'),
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
condition: 'on-failure',
|
condition: 'on-failure',
|
||||||
|
|||||||
@@ -30,19 +30,43 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cuid from 'cuid';
|
|
||||||
import { browser } from '$app/env';
|
|
||||||
import ServiceLinks from '$lib/components/ServiceLinks.svelte';
|
import ServiceLinks from '$lib/components/ServiceLinks.svelte';
|
||||||
import Services from './_Services/_Services.svelte';
|
import Services from './_Services/_Services.svelte';
|
||||||
|
import { get } from '$lib/api';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
|
||||||
export let service;
|
export let service;
|
||||||
export let isRunning;
|
export let isRunning;
|
||||||
export let readOnly;
|
export let readOnly;
|
||||||
export let settings;
|
export let settings;
|
||||||
|
|
||||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
const { id } = $page.params;
|
||||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
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(`/services/${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 h-20 items-center space-x-2 p-5 px-6 font-bold">
|
<div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
|
||||||
@@ -52,6 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="text-xs">{service.name}</span>
|
<span class="text-xs">{service.name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if service.fqdn}
|
{#if service.fqdn}
|
||||||
<a
|
<a
|
||||||
href={service.fqdn}
|
href={service.fqdn}
|
||||||
@@ -77,5 +102,31 @@
|
|||||||
|
|
||||||
<ServiceLinks {service} />
|
<ServiceLinks {service} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||||
|
<div class="text-2xl font-bold">Service 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>
|
||||||
<Services bind:service {isRunning} {readOnly} {settings} />
|
<Services bind:service {isRunning} {readOnly} {settings} />
|
||||||
|
|||||||
@@ -9,12 +9,17 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
|
|
||||||
let { name, fqdn, exposePort } = await event.request.json();
|
let {
|
||||||
|
name,
|
||||||
|
fqdn,
|
||||||
|
exposePort,
|
||||||
|
minio: { apiFqdn }
|
||||||
|
} = await event.request.json();
|
||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
if (exposePort) exposePort = Number(exposePort);
|
if (exposePort) exposePort = Number(exposePort);
|
||||||
|
if (apiFqdn) apiFqdn = apiFqdn.toLowerCase();
|
||||||
try {
|
try {
|
||||||
await db.updateService({ id, fqdn, name, exposePort });
|
await db.updateMinioService({ id, fqdn, apiFqdn, name, exposePort });
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as db from '$lib/database';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
import { startHttpProxy } from '$lib/haproxy';
|
|
||||||
import { ErrorHandler, getFreePort, getServiceImage } from '$lib/database';
|
import { ErrorHandler, getFreePort, 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';
|
||||||
@@ -35,7 +34,6 @@ export const post: RequestHandler = async (event) => {
|
|||||||
const publicPort = await getFreePort();
|
const publicPort = await getFreePort();
|
||||||
|
|
||||||
const consolePort = 9001;
|
const consolePort = 9001;
|
||||||
const apiPort = 9000;
|
|
||||||
|
|
||||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||||
const image = getServiceImage(type);
|
const image = getServiceImage(type);
|
||||||
@@ -65,7 +63,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
networks: [network],
|
networks: [network],
|
||||||
volumes: [config.volume],
|
volumes: [config.volume],
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort && { ports: [`${port}:${port}`] }),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
labels: makeLabelForServices('minio'),
|
labels: makeLabelForServices('minio'),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
@@ -94,8 +92,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
try {
|
try {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||||
await db.updateMinioService({ id, publicPort });
|
await db.updateMinioServicePort({ id, publicPort });
|
||||||
await startHttpProxy(destinationDocker, id, publicPort, apiPort);
|
|
||||||
return {
|
return {
|
||||||
status: 200
|
status: 200
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,12 +12,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const service = await db.getService({ id, teamId });
|
const service = await db.getService({ id, teamId });
|
||||||
const {
|
const { destinationDockerId, destinationDocker } = service;
|
||||||
destinationDockerId,
|
|
||||||
destinationDocker,
|
|
||||||
fqdn,
|
|
||||||
minio: { publicPort }
|
|
||||||
} = service;
|
|
||||||
await db.updateMinioService({ id, publicPort: null });
|
await db.updateMinioService({ id, publicPort: null });
|
||||||
if (destinationDockerId) {
|
if (destinationDockerId) {
|
||||||
const engine = destinationDocker.engine;
|
const engine = destinationDocker.engine;
|
||||||
@@ -30,11 +25,6 @@ export const post: RequestHandler = async (event) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await stopTcpHttpProxy(destinationDocker, publicPort);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
environment: config.environmentVariables,
|
environment: config.environmentVariables,
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
labels: makeLabelForServices('n8n'),
|
labels: makeLabelForServices('n8n'),
|
||||||
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
condition: 'on-failure',
|
condition: 'on-failure',
|
||||||
|
|||||||
@@ -12,15 +12,28 @@ export const post: RequestHandler = async (event) => {
|
|||||||
name,
|
name,
|
||||||
fqdn,
|
fqdn,
|
||||||
exposePort,
|
exposePort,
|
||||||
plausibleAnalytics: { email, username }
|
plausibleAnalytics: { email, username, scriptName }
|
||||||
} = await event.request.json();
|
} = await event.request.json();
|
||||||
|
|
||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
if (email) email = email.toLowerCase();
|
if (email) email = email.toLowerCase();
|
||||||
if (exposePort) exposePort = Number(exposePort);
|
if (exposePort) exposePort = Number(exposePort);
|
||||||
|
if (scriptName) {
|
||||||
|
scriptName = scriptName.toLowerCase();
|
||||||
|
if (scriptName.startsWith('/')) {
|
||||||
|
scriptName = scriptName.replaceAll(/\//gi, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await db.updatePlausibleAnalyticsService({ id, fqdn, name, email, username, exposePort });
|
await db.updatePlausibleAnalyticsService({
|
||||||
|
id,
|
||||||
|
fqdn,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
username,
|
||||||
|
exposePort,
|
||||||
|
scriptName
|
||||||
|
});
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
|||||||
networks: [network],
|
networks: [network],
|
||||||
environment: config.plausibleAnalytics.environmentVariables,
|
environment: config.plausibleAnalytics.environmentVariables,
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort && { ports: [`${port}:${exposePort}`] }),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
|
depends_on: [`${id}-postgresql`, `${id}-clickhouse`],
|
||||||
labels: makeLabelForServices('plausibleAnalytics'),
|
labels: makeLabelForServices('plausibleAnalytics'),
|
||||||
deploy: {
|
deploy: {
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
networks: [network],
|
networks: [network],
|
||||||
volumes: [],
|
volumes: [],
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort ? { ports: [`${port}:${port}`] } : {}),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
labels: makeLabelForServices('umami'),
|
labels: makeLabelForServices('umami'),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
|
|||||||
30
src/routes/services/[id]/usage.json.ts
Normal file
30
src/routes/services/[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 service = await db.getService({ id, teamId });
|
||||||
|
if (service.destinationDockerId) {
|
||||||
|
[usage] = await Promise.all([getContainerUsage(service.destinationDocker.engine, id)]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
usage
|
||||||
|
},
|
||||||
|
headers: {}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return ErrorHandler(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -78,7 +78,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
networks: [network],
|
networks: [network],
|
||||||
volumes: [config.volume, ...volumes],
|
volumes: [config.volume, ...volumes],
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort ? { ports: [`${port}:${exposePort}`] } : {}),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
labels: makeLabelForServices('vscodeServer'),
|
labels: makeLabelForServices('vscodeServer'),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
|
|||||||
187
src/routes/services/[id]/wordpress/ftp.json.ts
Normal file
187
src/routes/services/[id]/wordpress/ftp.json.ts
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import { dev } from '$app/env';
|
||||||
|
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
|
||||||
|
import { decrypt, encrypt } from '$lib/crypto';
|
||||||
|
import * as db from '$lib/database';
|
||||||
|
import { ErrorHandler, generatePassword, getFreePort } from '$lib/database';
|
||||||
|
import {
|
||||||
|
checkContainer,
|
||||||
|
startTcpProxy,
|
||||||
|
startTraefikTCPProxy,
|
||||||
|
stopTcpHttpProxy
|
||||||
|
} from '$lib/haproxy';
|
||||||
|
import type { ComposeFile } from '$lib/types/composeFile';
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import cuid from 'cuid';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
export const post: RequestHandler = async (event) => {
|
||||||
|
const { status, body, teamId } = await getUserDetails(event);
|
||||||
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
|
const { id } = event.params;
|
||||||
|
|
||||||
|
const { ftpEnabled } = await event.request.json();
|
||||||
|
const publicPort = await getFreePort();
|
||||||
|
|
||||||
|
let ftpUser = cuid();
|
||||||
|
let ftpPassword = generatePassword();
|
||||||
|
|
||||||
|
const hostkeyDir = dev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
||||||
|
try {
|
||||||
|
const data = await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpEnabled },
|
||||||
|
include: { service: { include: { destinationDocker: true } } }
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
service: { destinationDockerId, destinationDocker },
|
||||||
|
ftpPublicPort,
|
||||||
|
ftpUser: user,
|
||||||
|
ftpPassword: savedPassword,
|
||||||
|
ftpHostKey,
|
||||||
|
ftpHostKeyPrivate
|
||||||
|
} = data;
|
||||||
|
const { network, engine } = destinationDocker;
|
||||||
|
const settings = await db.prisma.setting.findFirst();
|
||||||
|
const host = getEngine(engine);
|
||||||
|
if (ftpEnabled) {
|
||||||
|
if (user) ftpUser = user;
|
||||||
|
if (savedPassword) ftpPassword = decrypt(savedPassword);
|
||||||
|
|
||||||
|
const { stdout: password } = await asyncExecShell(
|
||||||
|
`echo ${ftpPassword} | openssl passwd -1 -stdin`
|
||||||
|
);
|
||||||
|
if (destinationDockerId) {
|
||||||
|
try {
|
||||||
|
await fs.stat(hostkeyDir);
|
||||||
|
} catch (error) {
|
||||||
|
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
|
||||||
|
}
|
||||||
|
if (!ftpHostKey) {
|
||||||
|
await asyncExecShell(
|
||||||
|
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
|
||||||
|
);
|
||||||
|
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpHostKey: encrypt(ftpHostKey) }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
|
||||||
|
}
|
||||||
|
if (!ftpHostKeyPrivate) {
|
||||||
|
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
|
||||||
|
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: {
|
||||||
|
ftpPublicPort: publicPort,
|
||||||
|
ftpUser: user ? undefined : ftpUser,
|
||||||
|
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const isRunning = await checkContainer(engine, `${id}-ftp`);
|
||||||
|
if (isRunning) {
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
//
|
||||||
|
}
|
||||||
|
const volumes = [
|
||||||
|
`${id}-wordpress-data:/home/${ftpUser}`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
||||||
|
`${
|
||||||
|
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
|
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
||||||
|
];
|
||||||
|
|
||||||
|
const compose: ComposeFile = {
|
||||||
|
version: '3.8',
|
||||||
|
services: {
|
||||||
|
[`${id}-ftp`]: {
|
||||||
|
image: `atmoz/sftp:alpine`,
|
||||||
|
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`,
|
||||||
|
extra_hosts: ['host.docker.internal:host-gateway'],
|
||||||
|
container_name: `${id}-ftp`,
|
||||||
|
volumes,
|
||||||
|
networks: [network],
|
||||||
|
depends_on: [],
|
||||||
|
restart: 'always'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networks: {
|
||||||
|
[network]: {
|
||||||
|
external: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
volumes: {
|
||||||
|
[`${id}-wordpress-data`]: {
|
||||||
|
external: true,
|
||||||
|
name: `${id}-wordpress-data`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await fs.writeFile(
|
||||||
|
`${hostkeyDir}/${id}.sh`,
|
||||||
|
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key`
|
||||||
|
);
|
||||||
|
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
|
||||||
|
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: 201,
|
||||||
|
body: {
|
||||||
|
publicPort,
|
||||||
|
ftpUser,
|
||||||
|
ftpPassword
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
await db.prisma.wordpress.update({
|
||||||
|
where: { serviceId: id },
|
||||||
|
data: { ftpPublicPort: null }
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await asyncExecShell(
|
||||||
|
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
await stopTcpHttpProxy(id, destinationDocker, ftpPublicPort);
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return ErrorHandler(error);
|
||||||
|
} finally {
|
||||||
|
await asyncExecShell(
|
||||||
|
`rm -f ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -12,13 +12,26 @@ export const post: RequestHandler = async (event) => {
|
|||||||
name,
|
name,
|
||||||
fqdn,
|
fqdn,
|
||||||
exposePort,
|
exposePort,
|
||||||
wordpress: { extraConfig, mysqlDatabase }
|
ownMysql,
|
||||||
|
wordpress: { extraConfig, mysqlDatabase, mysqlHost, mysqlPort, mysqlUser, mysqlPassword }
|
||||||
} = await event.request.json();
|
} = await event.request.json();
|
||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||||
if (exposePort) exposePort = Number(exposePort);
|
if (exposePort) exposePort = Number(exposePort);
|
||||||
|
if (mysqlPort) mysqlPort = Number(mysqlPort);
|
||||||
try {
|
try {
|
||||||
await db.updateWordpress({ id, fqdn, name, extraConfig, mysqlDatabase, exposePort });
|
await db.updateWordpress({
|
||||||
|
id,
|
||||||
|
fqdn,
|
||||||
|
name,
|
||||||
|
extraConfig,
|
||||||
|
ownMysql,
|
||||||
|
mysqlDatabase,
|
||||||
|
exposePort,
|
||||||
|
mysqlHost,
|
||||||
|
mysqlPort,
|
||||||
|
mysqlUser,
|
||||||
|
mysqlPassword
|
||||||
|
});
|
||||||
return { status: 201 };
|
return { status: 201 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
|||||||
@@ -16,170 +16,17 @@ export const post: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
const { id } = event.params;
|
const { id } = event.params;
|
||||||
|
|
||||||
const { ftpEnabled } = await event.request.json();
|
const { ownMysql } = await event.request.json();
|
||||||
const publicPort = await getFreePort();
|
|
||||||
|
|
||||||
let ftpUser = cuid();
|
|
||||||
let ftpPassword = generatePassword();
|
|
||||||
|
|
||||||
const hostkeyDir = dev ? '/tmp/hostkeys' : '/app/ssl/hostkeys';
|
|
||||||
try {
|
try {
|
||||||
const data = await db.prisma.wordpress.update({
|
await db.prisma.wordpress.update({
|
||||||
where: { serviceId: id },
|
where: { serviceId: id },
|
||||||
data: { ftpEnabled },
|
data: { ownMysql }
|
||||||
include: { service: { include: { destinationDocker: true } } }
|
|
||||||
});
|
});
|
||||||
const {
|
return {
|
||||||
service: { destinationDockerId, destinationDocker },
|
status: 201
|
||||||
ftpPublicPort: oldPublicPort,
|
};
|
||||||
ftpUser: user,
|
|
||||||
ftpPassword: savedPassword,
|
|
||||||
ftpHostKey,
|
|
||||||
ftpHostKeyPrivate
|
|
||||||
} = data;
|
|
||||||
if (user) ftpUser = user;
|
|
||||||
if (savedPassword) ftpPassword = decrypt(savedPassword);
|
|
||||||
|
|
||||||
const { stdout: password } = await asyncExecShell(
|
|
||||||
`echo ${ftpPassword} | openssl passwd -1 -stdin`
|
|
||||||
);
|
|
||||||
if (destinationDockerId) {
|
|
||||||
try {
|
|
||||||
await fs.stat(hostkeyDir);
|
|
||||||
} catch (error) {
|
|
||||||
await asyncExecShell(`mkdir -p ${hostkeyDir}`);
|
|
||||||
}
|
|
||||||
if (!ftpHostKey) {
|
|
||||||
await asyncExecShell(
|
|
||||||
`ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519`
|
|
||||||
);
|
|
||||||
const { stdout: ftpHostKey } = await asyncExecShell(`cat ${hostkeyDir}/${id}.ed25519`);
|
|
||||||
await db.prisma.wordpress.update({
|
|
||||||
where: { serviceId: id },
|
|
||||||
data: { ftpHostKey: encrypt(ftpHostKey) }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await asyncExecShell(`echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`);
|
|
||||||
}
|
|
||||||
if (!ftpHostKeyPrivate) {
|
|
||||||
await asyncExecShell(`ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa`);
|
|
||||||
const { stdout: ftpHostKeyPrivate } = await asyncExecShell(`cat ${hostkeyDir}/${id}.rsa`);
|
|
||||||
await db.prisma.wordpress.update({
|
|
||||||
where: { serviceId: id },
|
|
||||||
data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await asyncExecShell(`echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`);
|
|
||||||
}
|
|
||||||
const { network, engine } = destinationDocker;
|
|
||||||
const host = getEngine(engine);
|
|
||||||
if (ftpEnabled) {
|
|
||||||
await db.prisma.wordpress.update({
|
|
||||||
where: { serviceId: id },
|
|
||||||
data: {
|
|
||||||
ftpPublicPort: publicPort,
|
|
||||||
ftpUser: user ? undefined : ftpUser,
|
|
||||||
ftpPassword: savedPassword ? undefined : encrypt(ftpPassword)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const isRunning = await checkContainer(engine, `${id}-ftp`);
|
|
||||||
if (isRunning) {
|
|
||||||
await asyncExecShell(
|
|
||||||
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
//
|
|
||||||
}
|
|
||||||
const volumes = [
|
|
||||||
`${id}-wordpress-data:/home/${ftpUser}`,
|
|
||||||
`${
|
|
||||||
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
|
||||||
}/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`,
|
|
||||||
`${
|
|
||||||
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
|
||||||
}/${id}.rsa:/etc/ssh/ssh_host_rsa_key`,
|
|
||||||
`${
|
|
||||||
dev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
|
||||||
}/${id}.sh:/etc/sftp.d/chmod.sh`
|
|
||||||
];
|
|
||||||
|
|
||||||
const compose: ComposeFile = {
|
|
||||||
version: '3.8',
|
|
||||||
services: {
|
|
||||||
[`${id}-ftp`]: {
|
|
||||||
image: `atmoz/sftp:alpine`,
|
|
||||||
command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`,
|
|
||||||
extra_hosts: ['host.docker.internal:host-gateway'],
|
|
||||||
container_name: `${id}-ftp`,
|
|
||||||
volumes,
|
|
||||||
networks: [network],
|
|
||||||
depends_on: [],
|
|
||||||
restart: 'always'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
networks: {
|
|
||||||
[network]: {
|
|
||||||
external: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
volumes: {
|
|
||||||
[`${id}-wordpress-data`]: {
|
|
||||||
external: true,
|
|
||||||
name: `${id}-wordpress-data`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await fs.writeFile(
|
|
||||||
`${hostkeyDir}/${id}.sh`,
|
|
||||||
`#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key`
|
|
||||||
);
|
|
||||||
await asyncExecShell(`chmod +x ${hostkeyDir}/${id}.sh`);
|
|
||||||
await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose));
|
|
||||||
await asyncExecShell(
|
|
||||||
`DOCKER_HOST=${host} docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d`
|
|
||||||
);
|
|
||||||
|
|
||||||
await startTcpProxy(destinationDocker, `${id}-ftp`, publicPort, 22);
|
|
||||||
} else {
|
|
||||||
await db.prisma.wordpress.update({
|
|
||||||
where: { serviceId: id },
|
|
||||||
data: { ftpPublicPort: null }
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
await asyncExecShell(
|
|
||||||
`DOCKER_HOST=${host} docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ftpEnabled) {
|
|
||||||
return {
|
|
||||||
status: 201,
|
|
||||||
body: {
|
|
||||||
publicPort,
|
|
||||||
ftpUser,
|
|
||||||
ftpPassword
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
} finally {
|
|
||||||
await asyncExecShell(
|
|
||||||
`rm -f ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ export const post: RequestHandler = async (event) => {
|
|||||||
exposePort,
|
exposePort,
|
||||||
wordpress: {
|
wordpress: {
|
||||||
mysqlDatabase,
|
mysqlDatabase,
|
||||||
|
mysqlHost,
|
||||||
|
mysqlPort,
|
||||||
mysqlUser,
|
mysqlUser,
|
||||||
mysqlPassword,
|
mysqlPassword,
|
||||||
extraConfig,
|
extraConfig,
|
||||||
mysqlRootUser,
|
mysqlRootUser,
|
||||||
mysqlRootUserPassword
|
mysqlRootUserPassword,
|
||||||
|
ownMysql
|
||||||
}
|
}
|
||||||
} = service;
|
} = service;
|
||||||
|
|
||||||
@@ -45,7 +48,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
image: `${image}:${version}`,
|
image: `${image}:${version}`,
|
||||||
volume: `${id}-wordpress-data:/var/www/html`,
|
volume: `${id}-wordpress-data:/var/www/html`,
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
WORDPRESS_DB_HOST: `${id}-mysql`,
|
WORDPRESS_DB_HOST: ownMysql ? `${mysqlHost}:${mysqlPort}` : `${id}-mysql`,
|
||||||
WORDPRESS_DB_USER: mysqlUser,
|
WORDPRESS_DB_USER: mysqlUser,
|
||||||
WORDPRESS_DB_PASSWORD: mysqlPassword,
|
WORDPRESS_DB_PASSWORD: mysqlPassword,
|
||||||
WORDPRESS_DB_NAME: mysqlDatabase,
|
WORDPRESS_DB_NAME: mysqlDatabase,
|
||||||
@@ -69,7 +72,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
config.wordpress.environmentVariables[secret.name] = secret.value;
|
config.wordpress.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const composeFile: ComposeFile = {
|
let composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
[id]: {
|
[id]: {
|
||||||
@@ -79,8 +82,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
volumes: [config.wordpress.volume],
|
volumes: [config.wordpress.volume],
|
||||||
networks: [network],
|
networks: [network],
|
||||||
restart: 'always',
|
restart: 'always',
|
||||||
...(exposePort ? { ports: [`${port}:${port}`] } : {}),
|
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||||
depends_on: [`${id}-mysql`],
|
|
||||||
labels: makeLabelForServices('wordpress'),
|
labels: makeLabelForServices('wordpress'),
|
||||||
deploy: {
|
deploy: {
|
||||||
restart_policy: {
|
restart_policy: {
|
||||||
@@ -90,22 +92,6 @@ export const post: RequestHandler = async (event) => {
|
|||||||
window: '120s'
|
window: '120s'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[`${id}-mysql`]: {
|
|
||||||
container_name: `${id}-mysql`,
|
|
||||||
image: config.mysql.image,
|
|
||||||
volumes: [config.mysql.volume],
|
|
||||||
environment: config.mysql.environmentVariables,
|
|
||||||
networks: [network],
|
|
||||||
restart: 'always',
|
|
||||||
deploy: {
|
|
||||||
restart_policy: {
|
|
||||||
condition: 'on-failure',
|
|
||||||
delay: '5s',
|
|
||||||
max_attempts: 3,
|
|
||||||
window: '120s'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
networks: {
|
networks: {
|
||||||
@@ -116,12 +102,32 @@ export const post: RequestHandler = async (event) => {
|
|||||||
volumes: {
|
volumes: {
|
||||||
[config.wordpress.volume.split(':')[0]]: {
|
[config.wordpress.volume.split(':')[0]]: {
|
||||||
name: config.wordpress.volume.split(':')[0]
|
name: config.wordpress.volume.split(':')[0]
|
||||||
},
|
|
||||||
[config.mysql.volume.split(':')[0]]: {
|
|
||||||
name: config.mysql.volume.split(':')[0]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (!ownMysql) {
|
||||||
|
composeFile.services[id].depends_on = [`${id}-mysql`];
|
||||||
|
composeFile.services[`${id}-mysql`] = {
|
||||||
|
container_name: `${id}-mysql`,
|
||||||
|
image: config.mysql.image,
|
||||||
|
volumes: [config.mysql.volume],
|
||||||
|
environment: config.mysql.environmentVariables,
|
||||||
|
networks: [network],
|
||||||
|
restart: 'always',
|
||||||
|
deploy: {
|
||||||
|
restart_policy: {
|
||||||
|
condition: 'on-failure',
|
||||||
|
delay: '5s',
|
||||||
|
max_attempts: 3,
|
||||||
|
window: '120s'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
composeFile.volumes[config.mysql.volume.split(':')[0]] = {
|
||||||
|
name: config.mysql.volume.split(':')[0]
|
||||||
|
};
|
||||||
|
}
|
||||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col flex-wrap justify-center">
|
<div class="flex-col justify-center">
|
||||||
{#if !services || ownServices.length === 0}
|
{#if !services || ownServices.length === 0}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="text-center text-xl font-bold">{$t('service.no_service')}</div>
|
<div class="text-center text-xl font-bold">{$t('service.no_service')}</div>
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ export const post: RequestHandler = async (event) => {
|
|||||||
minPort,
|
minPort,
|
||||||
maxPort,
|
maxPort,
|
||||||
isAutoUpdateEnabled,
|
isAutoUpdateEnabled,
|
||||||
isDNSCheckEnabled,
|
isDNSCheckEnabled
|
||||||
forceSave
|
|
||||||
} = await event.request.json();
|
} = await event.request.json();
|
||||||
try {
|
try {
|
||||||
const { id } = await db.listSettings();
|
const { id } = await db.listSettings();
|
||||||
|
|||||||
@@ -37,12 +37,13 @@
|
|||||||
import { getDomain } from '$lib/components/common';
|
import { getDomain } from '$lib/components/common';
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { features } from '$lib/store';
|
import { features, isTraefikUsed } from '$lib/store';
|
||||||
|
|
||||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||||
let dualCerts = settings.dualCerts;
|
let dualCerts = settings.dualCerts;
|
||||||
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||||
let isDNSCheckEnabled = settings.isDNSCheckEnabled;
|
let isDNSCheckEnabled = settings.isDNSCheckEnabled;
|
||||||
|
$isTraefikUsed = settings.isTraefikUsed;
|
||||||
|
|
||||||
let minPort = settings.minPort;
|
let minPort = settings.minPort;
|
||||||
let maxPort = settings.maxPort;
|
let maxPort = settings.maxPort;
|
||||||
@@ -55,7 +56,8 @@
|
|||||||
let isFqdnSet = !!settings.fqdn;
|
let isFqdnSet = !!settings.fqdn;
|
||||||
let loading = {
|
let loading = {
|
||||||
save: false,
|
save: false,
|
||||||
remove: false
|
remove: false,
|
||||||
|
proxyMigration: false
|
||||||
};
|
};
|
||||||
|
|
||||||
async function removeFqdn() {
|
async function removeFqdn() {
|
||||||
@@ -86,6 +88,7 @@
|
|||||||
if (name === 'isDNSCheckEnabled') {
|
if (name === 'isDNSCheckEnabled') {
|
||||||
isDNSCheckEnabled = !isDNSCheckEnabled;
|
isDNSCheckEnabled = !isDNSCheckEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
await post(`/settings.json`, {
|
await post(`/settings.json`, {
|
||||||
isRegistrationEnabled,
|
isRegistrationEnabled,
|
||||||
dualCerts,
|
dualCerts,
|
||||||
@@ -156,6 +159,20 @@
|
|||||||
function resetView() {
|
function resetView() {
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
}
|
}
|
||||||
|
async function migrateProxy(to) {
|
||||||
|
if (loading.proxyMigration) return;
|
||||||
|
try {
|
||||||
|
loading.proxyMigration = true;
|
||||||
|
await post(`/update.json`, { type: to });
|
||||||
|
const data = await get(`/settings.json`);
|
||||||
|
$isTraefikUsed = data.settings.isTraefikUsed;
|
||||||
|
return toast.push('Proxy migration started, it takes a few seconds.');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading.proxyMigration = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex space-x-1 p-6 font-bold">
|
||||||
@@ -192,6 +209,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-row gap-2 px-10">
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
<!-- <Language /> -->
|
<!-- <Language /> -->
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<div class="flex items-center py-2 pr-8">
|
||||||
|
<div class="flex w-96 flex-col">
|
||||||
|
<div class="text-xs font-bold text-stone-100 md:text-base">New Proxy Available!</div>
|
||||||
|
<Explainer
|
||||||
|
text="We are changing to <span class='text-sky-500 font-bold'>Traefik</span> as <span class='text-red-500 font-bold'>HAProxy</span> had several problems and uses a LOT of unnecessary memory (<span class='text-sky-500 font-bold'>~20MB</span> vs <span class='text-red-500 font-bold'>~200MB</span>).<br><br>You can switch back to HAProxy if something is not working and <span class='text-yellow-500 font-bold'>please let us know</span>!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
disabled={loading.proxyMigration}
|
||||||
|
class="bg-green-600 text-white hover:bg-green-500"
|
||||||
|
on:click={() => migrateProxy($isTraefikUsed ? 'haproxy' : 'traefik')}
|
||||||
|
>{loading.proxyMigration
|
||||||
|
? 'Migrating...'
|
||||||
|
: $isTraefikUsed
|
||||||
|
? 'Switch back to HAProxy'
|
||||||
|
: 'Migrate to Traefik'}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 items-start">
|
<div class="grid grid-cols-2 items-start">
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="pt-2 text-base font-bold text-stone-100">
|
<div class="pt-2 text-base font-bold text-stone-100">
|
||||||
|
|||||||
@@ -84,6 +84,9 @@
|
|||||||
<form on:submit|preventDefault={newGithubApp} class="py-4">
|
<form on:submit|preventDefault={newGithubApp} class="py-4">
|
||||||
<div class="flex space-x-1 pb-5 font-bold">
|
<div class="flex space-x-1 pb-5 font-bold">
|
||||||
<div class="title">General</div>
|
<div class="title">General</div>
|
||||||
|
{#if source.apiUrl && source.htmlUrl && source.name}
|
||||||
|
<button class=" bg-orange-600" type="submit">Create new GitHub App</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-row gap-2 px-10">
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
<div class="grid grid-flow-row gap-2">
|
<div class="grid grid-flow-row gap-2">
|
||||||
@@ -117,11 +120,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if source.apiUrl && source.htmlUrl && source.name}
|
|
||||||
<div class="text-center">
|
|
||||||
<button class=" mt-8 bg-orange-600" type="submit">Create new GitHub App</button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</form>
|
</form>
|
||||||
{:else if source.githubApp?.installationId}
|
{:else if source.githubApp?.installationId}
|
||||||
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
||||||
@@ -173,7 +171,7 @@
|
|||||||
</form>
|
</form>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button class=" bg-orange-600 mt-8" on:click={() => installRepositories(source)}
|
<button class=" mt-8 bg-orange-600" on:click={() => installRepositories(source)}
|
||||||
>Install Repositories</button
|
>Install Repositories</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -68,10 +68,48 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 px-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.git_source')}</div>
|
<div class="-mb-5 flex-col">
|
||||||
<span class="arrow-right-applications px-1 text-orange-500">></span>
|
<div class="md:max-w-64 truncate text-base tracking-tight md:text-2xl lg:block">
|
||||||
<span class="pr-2">{source.name}</span>
|
Configuration
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">{source.name}</span>
|
||||||
|
</div>
|
||||||
|
{#if source?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128" class="w-8">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if source?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128" class="w-8">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center">
|
<div class="flex flex-col justify-center">
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col flex-wrap justify-center">
|
<div class="flex-col justify-center">
|
||||||
{#if !sources || ownSources.length === 0}
|
{#if !sources || ownSources.length === 0}
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<div class="text-center text-xl font-bold">{$t('source.no_git_sources_found')}</div>
|
<div class="text-center text-xl font-bold">{$t('source.no_git_sources_found')}</div>
|
||||||
@@ -74,11 +74,48 @@
|
|||||||
{#each ownSources as source}
|
{#each ownSources as source}
|
||||||
<a href="/sources/{source.id}" class="w-96 p-2 no-underline">
|
<a href="/sources/{source.id}" class="w-96 p-2 no-underline">
|
||||||
<div
|
<div
|
||||||
class="box-selection group hover:bg-orange-600"
|
class="box-selection group relative hover:bg-orange-600"
|
||||||
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
|
||||||
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
class:border-0={source.gitlabApp && !source.gitlabAppId}
|
||||||
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
|
||||||
>
|
>
|
||||||
|
<div class="absolute top-0 left-0 -m-5 h-10 w-10">
|
||||||
|
{#if source?.type === 'gitlab'}
|
||||||
|
<svg viewBox="0 0 128 128">
|
||||||
|
<path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M126.615 72.31l-7.034-21.647L105.64 7.76c-.716-2.206-3.84-2.206-4.556 0l-13.94 42.903H40.856L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664 1.385 72.31a4.792 4.792 0 001.74 5.358L64 121.894l60.874-44.227a4.793 4.793 0 001.74-5.357"
|
||||||
|
/><path fill="#E24329" d="M64 121.894l23.144-71.23H40.856L64 121.893z" /><path
|
||||||
|
fill="#FC6D26"
|
||||||
|
d="M64 121.894l-23.144-71.23H8.42L64 121.893z"
|
||||||
|
/><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M8.42 50.663L1.384 72.31a4.79 4.79 0 001.74 5.357L64 121.894 8.42 50.664z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M8.42 50.663h32.436L26.916 7.76c-.717-2.206-3.84-2.206-4.557 0L8.42 50.664z"
|
||||||
|
/><path fill="#FC6D26" d="M64 121.894l23.144-71.23h32.437L64 121.893z" /><path
|
||||||
|
fill="#FCA326"
|
||||||
|
d="M119.58 50.663l7.035 21.647a4.79 4.79 0 01-1.74 5.357L64 121.894l55.58-71.23z"
|
||||||
|
/><path
|
||||||
|
fill="#E24329"
|
||||||
|
d="M119.58 50.663H87.145l13.94-42.902c.717-2.206 3.84-2.206 4.557 0l13.94 42.903z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else if source?.type === 'github'}
|
||||||
|
<svg viewBox="0 0 128 128">
|
||||||
|
<g fill="#ffffff"
|
||||||
|
><path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||||
|
/><path
|
||||||
|
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm2.446 2.729c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zM31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm3.261 3.361c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm4.5 1.951c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm4.943.361c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm4.598-.782c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||||
|
/></g
|
||||||
|
>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<div class="truncate text-center text-xl font-bold">{source.name}</div>
|
<div class="truncate text-center text-xl font-bold">{source.name}</div>
|
||||||
{#if $session.teamId === '0' && otherSources.length > 0}
|
{#if $session.teamId === '0' && otherSources.length > 0}
|
||||||
<div class="truncate text-center">{source.teams[0].name}</div>
|
<div class="truncate text-center">{source.teams[0].name}</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user