mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df58fcee16 | ||
|
|
ea3ffc429f | ||
|
|
2efca7a2b5 | ||
|
|
9db448a5e2 | ||
|
|
feee90beef | ||
|
|
906a63b6b5 | ||
|
|
2ce64ac213 | ||
|
|
4d8bf57135 | ||
|
|
c5348ce4b3 | ||
|
|
7f87c03f97 | ||
|
|
9469f148ff | ||
|
|
ffb7dc4ec2 | ||
|
|
242b8fa746 | ||
|
|
50cae5ac3b | ||
|
|
6a71233eb2 | ||
|
|
1aff8933c9 | ||
|
|
0ed87a5dfc | ||
|
|
24a6bcbd1e | ||
|
|
ca7f3da19d | ||
|
|
bf047e2a3c | ||
|
|
4454287be9 | ||
|
|
3bd2183655 | ||
|
|
1f7080e8f8 | ||
|
|
8b20761e8b | ||
|
|
655d0b5d5f | ||
|
|
91849cdd3a | ||
|
|
df25a694c3 | ||
|
|
eabaca145e | ||
|
|
2f0e458765 | ||
|
|
ff8037f231 | ||
|
|
a116028e1b | ||
|
|
e606a02b29 | ||
|
|
531c712ea5 | ||
|
|
3ae7624361 | ||
|
|
fed83462fa | ||
|
|
58c9f937c5 | ||
|
|
5d14b9209d | ||
|
|
305a95fa74 | ||
|
|
b29c1e702a | ||
|
|
b04d75ab08 | ||
|
|
25abfaadb9 | ||
|
|
1df81b8698 | ||
|
|
4487846fd7 | ||
|
|
86918f5160 | ||
|
|
bc723b3f15 | ||
|
|
1881e646d4 | ||
|
|
aa98808a1a | ||
|
|
f9a2232703 | ||
|
|
19d6be8663 | ||
|
|
0eb7c890ad | ||
|
|
7bfa68aa58 | ||
|
|
857a38050e | ||
|
|
c5b7f92caf | ||
|
|
df31ffd7fb | ||
|
|
0df0322d36 | ||
|
|
260552322d | ||
|
|
88ef6496a2 | ||
|
|
bdf123bf7b | ||
|
|
8fc3760eef | ||
|
|
5656f6f709 | ||
|
|
53e7e8b77e | ||
|
|
b990915b7a | ||
|
|
15b7822ffd | ||
|
|
cfa28419cb | ||
|
|
30ef0d2a3a | ||
|
|
755f99200a | ||
|
|
7af79ed3a2 | ||
|
|
2971e14269 | ||
|
|
01954aaf30 | ||
|
|
da018a8f2a | ||
|
|
77400bbbb0 | ||
|
|
3c3333d3df | ||
|
|
4963bd4144 | ||
|
|
b4a418dded | ||
|
|
a724b0daee | ||
|
|
88aa620cb4 | ||
|
|
70d3448110 | ||
|
|
09a1a406a6 | ||
|
|
40939d0b7f | ||
|
|
aec1d184c8 | ||
|
|
69d3cb5dd8 | ||
|
|
3deff162bb | ||
|
|
4ed54568d3 | ||
|
|
004724da55 | ||
|
|
97e9b5ffe3 | ||
|
|
45f920f802 | ||
|
|
2b31532d19 | ||
|
|
e7a6ecf95b | ||
|
|
545c98cee0 | ||
|
|
d29ccbfe37 | ||
|
|
d0807862e6 | ||
|
|
b92616dc14 | ||
|
|
a1a436300d | ||
|
|
16a5aeb1ba | ||
|
|
872095ff7a | ||
|
|
d88f2ea4c3 | ||
|
|
02e0385ab8 | ||
|
|
c9751d4cd9 | ||
|
|
162b637992 | ||
|
|
a10ddd4063 | ||
|
|
f46ccc63a7 | ||
|
|
fc04a45744 | ||
|
|
90c2b59a51 | ||
|
|
d6bee99c1b | ||
|
|
0871d47568 | ||
|
|
5c646c1898 | ||
|
|
8974de165f | ||
|
|
e622294b87 | ||
|
|
cf9d32b556 | ||
|
|
e2d6b5bf64 | ||
|
|
dec58fd6d1 | ||
|
|
dbb2241213 | ||
|
|
3bd8ac5820 | ||
|
|
f514aa676d | ||
|
|
73fc9755dd | ||
|
|
5089c843b6 | ||
|
|
cd527f2bce | ||
|
|
82de234f21 | ||
|
|
ae6f325c0a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
/yarn.lock
|
||||
|
||||
.env
|
||||
.env.prod
|
||||
|
||||
@@ -8,6 +8,8 @@ RUN yarn build
|
||||
FROM node:16.14.0-alpine
|
||||
WORKDIR /app
|
||||
|
||||
LABEL coolify.managed true
|
||||
|
||||
RUN apk add --no-cache git openssh-client curl jq cmake sqlite
|
||||
|
||||
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6
|
||||
|
||||
62
README.md
62
README.md
@@ -2,6 +2,66 @@
|
||||
|
||||
An open-source & self-hostable Heroku / Netlify alternative.
|
||||
|
||||
## Installation
|
||||
|
||||
Installation is automated with the following command:
|
||||
|
||||
```bash
|
||||
/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"
|
||||
```
|
||||
|
||||
## Migration from v1
|
||||
|
||||
A fresh installation is necessary. v2 is not compatible with v1.
|
||||
|
||||
## Features
|
||||
### Git Sources
|
||||
You can use the official ones or your self hosted version!
|
||||
|
||||
- Github
|
||||
- GitLab
|
||||
- Bitbucket (WIP)
|
||||
|
||||
### Destinations
|
||||
|
||||
- Local Docker Engine
|
||||
- Remote Docker Engine (WIP)
|
||||
- Kubernetes (WIP)
|
||||
|
||||
|
||||
### Applications
|
||||
|
||||
- Static sites
|
||||
- NodeJS
|
||||
- VueJS
|
||||
- NuxtJS
|
||||
- NextJS
|
||||
- React/Preact
|
||||
- NextJS
|
||||
- Gatsby
|
||||
- Svelte
|
||||
- PHP
|
||||
- Rust
|
||||
- Dockerfile (you can provide it)
|
||||
|
||||
### Databases
|
||||
|
||||
- MongoDB
|
||||
- MySQL
|
||||
- PostgreSQL
|
||||
- CouchDB
|
||||
- Redis
|
||||
|
||||
### One-click services
|
||||
|
||||
- [WordPress](https://wordpress.org)
|
||||
- [Plausible Analytics](https://plausible.io)
|
||||
- [NocoDB](https://nocodb.com)
|
||||
- [VSCode Server](https://github.com/cdr/code-server)
|
||||
- [MinIO](https://min.io)
|
||||
- [VaultWarden](https://github.com/dani-garcia/vaultwarden)
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai)
|
||||
@@ -11,7 +71,7 @@ An open-source & self-hostable Heroku / Netlify alternative.
|
||||
|
||||
## Roadmap
|
||||
|
||||
[See the Roadmap here](https://github.com/coollabsio/coolify/projects/1)
|
||||
[See the Roadmap here](https://github.com/orgs/coollabsio/projects/3/views/8)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
coolify:
|
||||
image: coollabsio/coolify:latest
|
||||
image: coollabsio/coolify:${TAG:-latest}
|
||||
restart: always
|
||||
container_name: coolify
|
||||
ports:
|
||||
|
||||
40
package.json
40
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.14",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
||||
"dev:stop": "docker-compose -f docker-compose-dev.yaml down",
|
||||
"dev:logs": "docker-compose -f docker-compose-dev.yaml logs -f --tail 10",
|
||||
"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
||||
"dev:stop": "docker compose -f docker-compose-dev.yaml down",
|
||||
"dev:logs": "docker compose -f docker-compose-dev.yaml logs -f --tail 10",
|
||||
"studio": "npx prisma studio",
|
||||
"start": "npx prisma migrate deploy && npx prisma generate && npx prisma db seed && node index.js",
|
||||
"build": "svelte-kit build",
|
||||
@@ -16,20 +16,21 @@
|
||||
"db:generate": "prisma generate",
|
||||
"db:push": "prisma db push && prisma generate",
|
||||
"db:seed": "prisma db seed",
|
||||
"prerelease": "cross-var docker build -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest .",
|
||||
"release:coolify": "cross-var yarn prerelease && docker push coollabsio/coolify:$npm_package_version && docker image push coollabsio/coolify:$npm_package_version && docker push coollabsio/coolify:latest",
|
||||
"release:staging": "cross-var docker build -t coollabsio/coolify:$npm_package_version . && docker push coollabsio/coolify:$npm_package_version",
|
||||
"release:pre": "cross-var docker build -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest .",
|
||||
"release:coolify": "cross-var yarn release:pre && docker push coollabsio/coolify:$npm_package_version && docker push coollabsio/coolify:latest",
|
||||
"release:haproxy": "docker build -f haproxy.Dockerfile -t coollabsio/coolify-haproxy-alpine:1.0.0 -t coollabsio/coolify-haproxy-alpine:latest . && docker image push --all-tags coollabsio/coolify-haproxy-alpine",
|
||||
"release:haproxy:tcp": "docker build -f haproxy-tcp.Dockerfile -t coollabsio/coolify-haproxy-tcp-alpine:1.0.0 -t coollabsio/coolify-haproxy-tcp-alpine:latest . && docker image push --all-tags coollabsio/coolify-haproxy-tcp-alpine",
|
||||
"release:haproxy:http": "docker build -f haproxy-http.Dockerfile -t coollabsio/coolify-haproxy-http-alpine:1.0.0 -t coollabsio/coolify-haproxy-http-alpine:latest . && docker image push --all-tags coollabsio/coolify-haproxy-http-alpine",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "1.0.0-next.67",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.27",
|
||||
"@sveltejs/kit": "1.0.0-next.259",
|
||||
"@sveltejs/adapter-node": "1.0.0-next.68",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.28",
|
||||
"@sveltejs/kit": "1.0.0-next.278",
|
||||
"@types/bcrypt": "5.0.0",
|
||||
"@types/js-cookie": "3.0.1",
|
||||
"@types/node": "17.0.16",
|
||||
"@types/node": "17.0.18",
|
||||
"@types/node-forge": "1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.31.1",
|
||||
"@typescript-eslint/parser": "4.31.1",
|
||||
@@ -40,16 +41,16 @@
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-svelte3": "3.2.1",
|
||||
"husky": "7.0.4",
|
||||
"lint-staged": "12.3.3",
|
||||
"lint-staged": "12.3.4",
|
||||
"postcss": "8.4.6",
|
||||
"prettier": "2.5.1",
|
||||
"prettier-plugin-svelte": "2.6.0",
|
||||
"prettier-plugin-tailwindcss": "0.1.7",
|
||||
"prisma": "3.9.1",
|
||||
"prisma": "3.9.2",
|
||||
"svelte": "3.46.4",
|
||||
"svelte-check": "2.4.3",
|
||||
"svelte-preprocess": "4.10.3",
|
||||
"tailwindcss": "3.0.19",
|
||||
"tailwindcss": "3.0.23",
|
||||
"ts-node": "10.5.0",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.5.5"
|
||||
@@ -57,10 +58,10 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@prisma/client": "3.9.1",
|
||||
"@sentry/node": "6.17.6",
|
||||
"@prisma/client": "3.9.2",
|
||||
"@sentry/node": "6.17.9",
|
||||
"bcrypt": "5.0.1",
|
||||
"bullmq": "1.69.0",
|
||||
"bullmq": "1.73.0",
|
||||
"compare-versions": "4.1.3",
|
||||
"cookie": "0.4.2",
|
||||
"cuid": "2.1.8",
|
||||
@@ -68,14 +69,15 @@
|
||||
"dockerode": "3.3.1",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"generate-password": "1.7.0",
|
||||
"get-port": "6.0.0",
|
||||
"get-port": "6.1.0",
|
||||
"got": "12.0.1",
|
||||
"js-cookie": "3.0.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"node-forge": "1.2.1",
|
||||
"svelte-kit-cookie-session": "2.0.3",
|
||||
"unique-names-generator": "4.6.0"
|
||||
"svelte-kit-cookie-session": "2.1.2",
|
||||
"tailwindcss-scrollbar": "^0.1.0",
|
||||
"unique-names-generator": "4.7.1"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "node prisma/seed.cjs"
|
||||
|
||||
200
pnpm-lock.yaml
generated
200
pnpm-lock.yaml
generated
@@ -2,21 +2,21 @@ lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
'@iarna/toml': 2.2.5
|
||||
'@prisma/client': 3.9.1
|
||||
'@sentry/node': 6.17.6
|
||||
'@sveltejs/adapter-node': 1.0.0-next.67
|
||||
'@sveltejs/adapter-static': 1.0.0-next.27
|
||||
'@sveltejs/kit': 1.0.0-next.259
|
||||
'@prisma/client': 3.9.2
|
||||
'@sentry/node': 6.17.9
|
||||
'@sveltejs/adapter-node': 1.0.0-next.68
|
||||
'@sveltejs/adapter-static': 1.0.0-next.28
|
||||
'@sveltejs/kit': 1.0.0-next.278
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
'@types/node-forge': 1.0.0
|
||||
'@typescript-eslint/eslint-plugin': 4.31.1
|
||||
'@typescript-eslint/parser': 4.31.1
|
||||
'@zerodevx/svelte-toast': 0.6.3
|
||||
autoprefixer: 10.4.2
|
||||
bcrypt: 5.0.1
|
||||
bullmq: 1.69.0
|
||||
bullmq: 1.73.0
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cross-var: 1.1.0
|
||||
@@ -28,35 +28,36 @@ specifiers:
|
||||
eslint-config-prettier: 8.3.0
|
||||
eslint-plugin-svelte3: 3.2.1
|
||||
generate-password: 1.7.0
|
||||
get-port: 6.0.0
|
||||
get-port: 6.1.0
|
||||
got: 12.0.1
|
||||
husky: 7.0.4
|
||||
js-cookie: 3.0.1
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
lint-staged: 12.3.3
|
||||
lint-staged: 12.3.4
|
||||
node-forge: 1.2.1
|
||||
postcss: 8.4.6
|
||||
prettier: 2.5.1
|
||||
prettier-plugin-svelte: 2.6.0
|
||||
prettier-plugin-tailwindcss: 0.1.7
|
||||
prisma: 3.9.1
|
||||
prisma: 3.9.2
|
||||
svelte: 3.46.4
|
||||
svelte-check: 2.4.3
|
||||
svelte-kit-cookie-session: 2.0.3
|
||||
svelte-kit-cookie-session: 2.1.2
|
||||
svelte-preprocess: 4.10.3
|
||||
tailwindcss: 3.0.19
|
||||
tailwindcss: 3.0.23
|
||||
tailwindcss-scrollbar: ^0.1.0
|
||||
ts-node: 10.5.0
|
||||
tslib: 2.3.1
|
||||
typescript: 4.5.5
|
||||
unique-names-generator: 4.6.0
|
||||
unique-names-generator: 4.7.1
|
||||
|
||||
dependencies:
|
||||
'@iarna/toml': 2.2.5
|
||||
'@prisma/client': 3.9.1_prisma@3.9.1
|
||||
'@sentry/node': 6.17.6
|
||||
'@prisma/client': 3.9.2_prisma@3.9.2
|
||||
'@sentry/node': 6.17.9
|
||||
bcrypt: 5.0.1
|
||||
bullmq: 1.69.0
|
||||
bullmq: 1.73.0
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cuid: 2.1.8
|
||||
@@ -64,22 +65,23 @@ dependencies:
|
||||
dockerode: 3.3.1
|
||||
dotenv-extended: 2.9.0
|
||||
generate-password: 1.7.0
|
||||
get-port: 6.0.0
|
||||
get-port: 6.1.0
|
||||
got: 12.0.1
|
||||
js-cookie: 3.0.1
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
node-forge: 1.2.1
|
||||
svelte-kit-cookie-session: 2.0.3
|
||||
unique-names-generator: 4.6.0
|
||||
svelte-kit-cookie-session: 2.1.2
|
||||
tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23
|
||||
unique-names-generator: 4.7.1
|
||||
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-node': 1.0.0-next.67
|
||||
'@sveltejs/adapter-static': 1.0.0-next.27
|
||||
'@sveltejs/kit': 1.0.0-next.259_svelte@3.46.4
|
||||
'@sveltejs/adapter-node': 1.0.0-next.68
|
||||
'@sveltejs/adapter-static': 1.0.0-next.28
|
||||
'@sveltejs/kit': 1.0.0-next.278_svelte@3.46.4
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
'@types/node-forge': 1.0.0
|
||||
'@typescript-eslint/eslint-plugin': 4.31.1_5d7752337e5ea49772097d8af1823bf9
|
||||
'@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.5.5
|
||||
@@ -90,17 +92,17 @@ devDependencies:
|
||||
eslint-config-prettier: 8.3.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.46.4
|
||||
husky: 7.0.4
|
||||
lint-staged: 12.3.3
|
||||
lint-staged: 12.3.4
|
||||
postcss: 8.4.6
|
||||
prettier: 2.5.1
|
||||
prettier-plugin-svelte: 2.6.0_prettier@2.5.1+svelte@3.46.4
|
||||
prettier-plugin-tailwindcss: 0.1.7_prettier@2.5.1
|
||||
prisma: 3.9.1
|
||||
prisma: 3.9.2
|
||||
svelte: 3.46.4
|
||||
svelte-check: 2.4.3_postcss@8.4.6+svelte@3.46.4
|
||||
svelte-preprocess: 4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9
|
||||
tailwindcss: 3.0.19_27d966e3a2f4b84fbc8a2f9653dbb362
|
||||
ts-node: 10.5.0_99ae9436e134a034c8d45fdd98ebbf22
|
||||
tailwindcss: 3.0.23_c940fbabf228b85b1c73d314b43e31f1
|
||||
ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2
|
||||
tslib: 2.3.1
|
||||
typescript: 4.5.5
|
||||
|
||||
@@ -250,10 +252,10 @@ packages:
|
||||
fastq: 1.13.0
|
||||
dev: true
|
||||
|
||||
/@prisma/client/3.9.1_prisma@3.9.1:
|
||||
/@prisma/client/3.9.2_prisma@3.9.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-aLwfXKLvL+loQ0IuPPCXkcq8cXBg1IeoHHa5lqQu3dJHdj45wnislA/Ny4UxRQjD5FXqrfAb8sWtF+jhdmjFTg==
|
||||
integrity: sha512-VlEIYVMyfFZHbVBOlunPl47gmP/Z0zzPjPj8I7uKEIaABqrUy50ru3XS0aZd8GFvevVwt7p91xxkUjNjrWhKAQ==
|
||||
}
|
||||
engines: { node: '>=12.6' }
|
||||
requiresBuild: true
|
||||
@@ -264,7 +266,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@prisma/engines-version': 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009
|
||||
prisma: 3.9.1
|
||||
prisma: 3.9.2
|
||||
dev: false
|
||||
|
||||
/@prisma/engines-version/3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009:
|
||||
@@ -293,56 +295,56 @@ packages:
|
||||
picomatch: 2.3.0
|
||||
dev: true
|
||||
|
||||
/@sentry/core/6.17.6:
|
||||
/@sentry/core/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-wSNsQSqsW8vQ2HEvUEXYOJnzTyVDSWbyH4RHrWV1pQM8zqGx/qfz0sKFM5XFnE9ZeaXKL8LXV3v5i73v+z8lew==
|
||||
integrity: sha512-14KalmTholGUtgdh9TklO+jUpyQ/D3OGkhlH1rnGQGoJgFy2eYm+s+MnUEMxFdGIUCz5kOteuNqYZxaDmFagpQ==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/hub': 6.17.6
|
||||
'@sentry/minimal': 6.17.6
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/utils': 6.17.6
|
||||
'@sentry/hub': 6.17.9
|
||||
'@sentry/minimal': 6.17.9
|
||||
'@sentry/types': 6.17.9
|
||||
'@sentry/utils': 6.17.9
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
/@sentry/hub/6.17.6:
|
||||
/@sentry/hub/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Ps9nk+DoFia8jhZ1lucdRE0vDx8hqXOsKXJE8a3hK/Ndki0J9jedYqBeLqSgiFG4qRjXpNFcD6TEM6tnQrv5lw==
|
||||
integrity: sha512-34EdrweWDbBV9EzEFIXcO+JeoyQmKzQVJxpTKZoJA6PUwf2NrndaUdjlkDEtBEzjuLUTxhLxtOzEsYs1O6RVcg==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/utils': 6.17.6
|
||||
'@sentry/types': 6.17.9
|
||||
'@sentry/utils': 6.17.9
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
/@sentry/minimal/6.17.6:
|
||||
/@sentry/minimal/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-PLGf8WlhtdHuY6ofwYR3nyClr/TYHHAW6i0r62OZCOXTqnFPJorZpAz3VCCP2jMJmbgVbo03wN+u/xAA/zwObA==
|
||||
integrity: sha512-T3PMCHcKk6lkZq6zKgANrYJJxXBXKOe+ousV1Fas1rVBMv7dtKfsa4itqQHszcW9shusPDiaQKIJ4zRLE5LKmg==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/hub': 6.17.6
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/hub': 6.17.9
|
||||
'@sentry/types': 6.17.9
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
/@sentry/node/6.17.6:
|
||||
/@sentry/node/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-T1s0yPbGvYpoh9pJgLvpy7s+jVwCyf0ieEoN9rSbnPwbi2vm6MfoV5wtGrE0cBHTPgnyOMv+zq4Q3ww6dfr7Pw==
|
||||
integrity: sha512-jbn+q7qPGOh6D7nYoYGaAlmuvMDpQmyMwBtUVYybuZp2AALe43O3Z4LtoJ+1+F31XowpsIPZx1mwNs4ZrILskA==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/core': 6.17.6
|
||||
'@sentry/hub': 6.17.6
|
||||
'@sentry/tracing': 6.17.6
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/utils': 6.17.6
|
||||
'@sentry/core': 6.17.9
|
||||
'@sentry/hub': 6.17.9
|
||||
'@sentry/tracing': 6.17.9
|
||||
'@sentry/types': 6.17.9
|
||||
'@sentry/utils': 6.17.9
|
||||
cookie: 0.4.2
|
||||
https-proxy-agent: 5.0.0
|
||||
lru_map: 0.3.3
|
||||
@@ -351,36 +353,36 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@sentry/tracing/6.17.6:
|
||||
/@sentry/tracing/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+h5ov+zEm5WH9+vmFfdT4EIqBOW7Tggzh0BDz8QRStRc2JbvEiSZDs+HlsycBwWMQi/ucJs93FPtNnWjW+xvBw==
|
||||
integrity: sha512-5Rb/OS4ryNJLvz2nv6wyjwhifjy6veqaF9ffLrwFYij/WDy7m62ASBblxgeiI3fbPLX0aBRFWIJAq1vko26+AQ==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/hub': 6.17.6
|
||||
'@sentry/minimal': 6.17.6
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/utils': 6.17.6
|
||||
'@sentry/hub': 6.17.9
|
||||
'@sentry/minimal': 6.17.9
|
||||
'@sentry/types': 6.17.9
|
||||
'@sentry/utils': 6.17.9
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
/@sentry/types/6.17.6:
|
||||
/@sentry/types/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-peGM873lDJtHd/jwW9Egr/hhxLuF0bcPIf2kMZlvEvW/G5GCbuaCR4ArQJlh7vQyma+NLn/XdojpJkC0TomKrw==
|
||||
integrity: sha512-xuulX6qUCL14ayEOh/h6FUIvZtsi1Bx34dSOaWDrjXUOJHJAM7214uiqW1GZxPJ13YuaUIubjTSfDmSQ9CBzTw==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dev: false
|
||||
|
||||
/@sentry/utils/6.17.6:
|
||||
/@sentry/utils/6.17.9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-RI797N8Ax5yuKUftVX6dc0XmXqo5CN7XqJYPFzYC8udutQ4L8ZYadtUcqNsdz1ZQxl+rp0XK9Q6wjoWmsI2RXA==
|
||||
integrity: sha512-4eo9Z3JlJCGlGrQRbtZWL+L9NnlUXgTbfK3Lk7oO8D1ev8R5b5+iE6tZHTvU5rQRcq6zu+POT+tK5u9oxc/rnQ==
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
'@sentry/types': 6.17.6
|
||||
'@sentry/types': 6.17.9
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
@@ -392,28 +394,28 @@ packages:
|
||||
engines: { node: '>=10' }
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-node/1.0.0-next.67:
|
||||
/@sveltejs/adapter-node/1.0.0-next.68:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+LuLn91xARZsRANiQNIIDpMMncUTnP2pJc8tyL+FdpVvs5UtlvkYJpeCBPFqjjseRpIIbi8Slu89GCdrRXBDUg==
|
||||
integrity: sha512-MiEjtl15Aupm6bjirVlq0kkc9AL8qDXz/blsh4jYMsaiidmcEHeDgfZQFM5YiXy95DbxV30MAkhwCQiYK/J8Kw==
|
||||
}
|
||||
dependencies:
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-static/1.0.0-next.27:
|
||||
/@sveltejs/adapter-static/1.0.0-next.28:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-dcN1p1D7ZY/a9SClfN14mgm9pyWbLxdwM9gzPMZG6xXOoqMtwI03aZOFgGGumHPdv+XcGRZM96vUSRoDm6vBJQ==
|
||||
integrity: sha512-c4xLyeSwnbGQxe4f1SLpHTbxZDm3TEr43scR3tOlVgQN+mnAL9aDdl3nTtdzWmrUDmDEmY4GriAwLyFLZuINLw==
|
||||
}
|
||||
dependencies:
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.259_svelte@3.46.4:
|
||||
/@sveltejs/kit/1.0.0-next.278_svelte@3.46.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+Tss6cQXmpi4Jno/ZP0zJ3INBLMED+WeW4UI81tmexheC76Y2p+cbInneKO/REx/8QFo1iroYrWAUkZPsOg8Ew==
|
||||
integrity: sha512-WT93Wnu05X9WG9BMMk/dj0gy6R7iXm9aXRDVgmIl9z8jT2ukejgmkhi5IwBYrK0OMIUALRVfukn+iy+srPc91Q==
|
||||
}
|
||||
engines: { node: '>=14.13' }
|
||||
hasBin: true
|
||||
@@ -502,7 +504,7 @@ packages:
|
||||
integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
dev: true
|
||||
|
||||
/@types/cacheable-request/6.0.2:
|
||||
@@ -513,7 +515,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.1
|
||||
'@types/keyv': 3.1.3
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
'@types/responselike': 1.0.0
|
||||
dev: false
|
||||
|
||||
@@ -544,7 +546,7 @@ packages:
|
||||
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
dev: false
|
||||
|
||||
/@types/node-forge/1.0.0:
|
||||
@@ -553,13 +555,13 @@ packages:
|
||||
integrity: sha512-h0bgwPKq5u99T9Gor4qtV1lCZ41xNkai0pie1n/a2mh2/4+jENWOlo7AJ4YKxTZAnSZ8FRurUpdIN7ohaPPuHA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
dev: true
|
||||
|
||||
/@types/node/17.0.16:
|
||||
/@types/node/17.0.18:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ydLaGVfQOQ6hI1xK2A5nVh8bl0OGoIfYMxPWHqqYe9bTkWCfqiVvZoh2I/QF2sNSkZzZyROBoTefIEI+PB6iIA==
|
||||
integrity: sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==
|
||||
}
|
||||
|
||||
/@types/parse-json/4.0.0:
|
||||
@@ -582,7 +584,7 @@ packages:
|
||||
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
dev: false
|
||||
|
||||
/@types/sass/1.16.1:
|
||||
@@ -591,7 +593,7 @@ packages:
|
||||
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.31.1_5d7752337e5ea49772097d8af1823bf9:
|
||||
@@ -1746,10 +1748,10 @@ packages:
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/bullmq/1.69.0:
|
||||
/bullmq/1.73.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-1aIO7bN0HQeADWoXa+I72GgofvoBFRs/kcoveB3KN8ytKv7QJbGhtks0pYNhHn/P9H3OWHWDccpNEfnv3VGfcw==
|
||||
integrity: sha512-+BF7yeGagYD/iMkM3FA8Wvb3j3MyKE/OdXv404+nQjUsKXfL7PbqX5NSA9lBtFzOdyFx9ZWyKRnBwuGQsLfM0w==
|
||||
}
|
||||
dependencies:
|
||||
cron-parser: 2.18.0
|
||||
@@ -3114,10 +3116,10 @@ packages:
|
||||
engines: { node: '>=8' }
|
||||
dev: false
|
||||
|
||||
/get-port/6.0.0:
|
||||
/get-port/6.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qSVkVF6Eq1GdL/cBNiFuP4nUHMF7OEMTqEjC6alR2N90u8BFOoO0PFhNTX2QtAUoGrz8NnrSWj85TZ8YXZ6LOA==
|
||||
integrity: sha512-JKnPFW/G2ZRirH/25sLK1aLBQktJfQLixzMMuMBP8A2G/ivSaIwdTnlJeO7PWeyhyIGVorezNf6+CXZU9i0cIQ==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dev: false
|
||||
@@ -3718,10 +3720,10 @@ packages:
|
||||
resolution: { integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= }
|
||||
dev: true
|
||||
|
||||
/lint-staged/12.3.3:
|
||||
/lint-staged/12.3.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-OqcLsqcPOqzvsfkxjeBpZylgJ3SRG1RYqc9LxC6tkt6tNsq1bNVkAixBwX09f6CobcHswzqVOCBpFR1Fck0+ag==
|
||||
integrity: sha512-yv/iK4WwZ7/v0GtVkNb3R82pdL9M+ScpIbJLJNyCXkJ1FGaXvRCOg/SeL59SZtPpqZhE7BD6kPKFLIDUhDx2/w==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
hasBin: true
|
||||
@@ -4397,7 +4399,7 @@ packages:
|
||||
dependencies:
|
||||
import-cwd: 3.0.0
|
||||
lilconfig: 2.0.4
|
||||
ts-node: 10.5.0_99ae9436e134a034c8d45fdd98ebbf22
|
||||
ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
@@ -4486,10 +4488,10 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/prisma/3.9.1:
|
||||
/prisma/3.9.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw==
|
||||
integrity: sha512-i9eK6cexV74OgeWaH3+e6S07kvC9jEZTl6BqtBH398nlCU0tck7mE9dicY6YQd+euvMjjCtY89q4NgmaPnUsSg==
|
||||
}
|
||||
engines: { node: '>=12.6' }
|
||||
hasBin: true
|
||||
@@ -5203,10 +5205,10 @@ packages:
|
||||
svelte: 3.46.4
|
||||
dev: true
|
||||
|
||||
/svelte-kit-cookie-session/2.0.3:
|
||||
/svelte-kit-cookie-session/2.1.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-jOBUpvrkt/fI5zaqWsWHDDIGnfuPQt3/PC1FDJpEV/E/hA8DvGO52esFny1HvUAP1tkVZ5FU3k6Yd3HyQH5oUQ==
|
||||
integrity: sha512-PfxIWDhiyYWu7iKlL0GHpmwDrdFh+rX/WmBzOuvctF25UqngIo9MCiegWBSBLE1RBwNs5UqaIeI8+vligmY07g==
|
||||
}
|
||||
dev: false
|
||||
|
||||
@@ -5288,16 +5290,26 @@ packages:
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/tailwindcss/3.0.19_27d966e3a2f4b84fbc8a2f9653dbb362:
|
||||
/tailwindcss-scrollbar/0.1.0_tailwindcss@3.0.23:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-rjsdfz/qZya5xQ0OVynEMETgWq1CacmftgMYeXXh6bRM5vxsNwRSbMJsCCIjq/w67om9VP/AFMolOwiE+5VKig==
|
||||
integrity: sha512-egipxw4ooQDh94x02XQpPck0P0sfwazwoUGfA9SedPATIuYDR+6qe8d31Gl7YsSMRiOKDkkqfI0kBvEw9lT/Hg==
|
||||
}
|
||||
peerDependencies:
|
||||
tailwindcss: '>= 2.x.x'
|
||||
dependencies:
|
||||
tailwindcss: 3.0.23_c940fbabf228b85b1c73d314b43e31f1
|
||||
dev: false
|
||||
|
||||
/tailwindcss/3.0.23_c940fbabf228b85b1c73d314b43e31f1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==
|
||||
}
|
||||
engines: { node: '>=12.13.0' }
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
autoprefixer: ^10.0.2
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
arg: 5.0.1
|
||||
autoprefixer: 10.4.2_postcss@8.4.6
|
||||
@@ -5408,7 +5420,7 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/ts-node/10.5.0_99ae9436e134a034c8d45fdd98ebbf22:
|
||||
/ts-node/10.5.0_f3bd4037939c2ed2942ba074291f8ef2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==
|
||||
@@ -5430,7 +5442,7 @@ packages:
|
||||
'@tsconfig/node12': 1.0.9
|
||||
'@tsconfig/node14': 1.0.1
|
||||
'@tsconfig/node16': 1.0.2
|
||||
'@types/node': 17.0.16
|
||||
'@types/node': 17.0.18
|
||||
acorn: 8.5.0
|
||||
acorn-walk: 8.2.0
|
||||
arg: 4.1.3
|
||||
@@ -5516,10 +5528,10 @@ packages:
|
||||
function.name: 1.0.13
|
||||
dev: false
|
||||
|
||||
/unique-names-generator/4.6.0:
|
||||
/unique-names-generator/4.7.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-m0fke1emBeT96UYn2psPQYwljooDWRTKt9oUZ5vlt88ZFMBGxqwPyLHXwCfkbgdm8jzioCp7oIpo6KdM+fnUlQ==
|
||||
integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dev: false
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[name,applicationId]` on the table `Secret` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- DropIndex
|
||||
DROP INDEX "Secret_name_key";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Secret_name_applicationId_key" ON "Secret"("name", "applicationId");
|
||||
47
prisma/migrations/20220217211304_dualcerts/migration.sql
Normal file
47
prisma/migrations/20220217211304_dualcerts/migration.sql
Normal file
@@ -0,0 +1,47 @@
|
||||
-- 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,
|
||||
"proxyPassword" TEXT NOT NULL,
|
||||
"proxyUser" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting";
|
||||
DROP TABLE "Setting";
|
||||
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||
CREATE TABLE "new_ApplicationSettings" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"applicationId" TEXT NOT NULL,
|
||||
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_ApplicationSettings" ("applicationId", "createdAt", "debug", "id", "previews", "updatedAt") SELECT "applicationId", "createdAt", "debug", "id", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||
DROP TABLE "ApplicationSettings";
|
||||
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||
CREATE TABLE "new_Service" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"fqdn" TEXT,
|
||||
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||
"type" TEXT,
|
||||
"version" TEXT,
|
||||
"destinationDockerId" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Service" ("createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version" FROM "Service";
|
||||
DROP TABLE "Service";
|
||||
ALTER TABLE "new_Service" RENAME TO "Service";
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -11,6 +11,7 @@ model Setting {
|
||||
id String @id @default(cuid())
|
||||
fqdn String? @unique
|
||||
isRegistrationEnabled Boolean @default(false)
|
||||
dualCerts Boolean @default(false)
|
||||
proxyPassword String
|
||||
proxyUser String
|
||||
createdAt DateTime @default(now())
|
||||
@@ -97,6 +98,7 @@ model ApplicationSettings {
|
||||
id String @id @default(cuid())
|
||||
application Application @relation(fields: [applicationId], references: [id])
|
||||
applicationId String @unique
|
||||
dualCerts Boolean @default(false)
|
||||
debug Boolean @default(false)
|
||||
previews Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
@@ -105,13 +107,15 @@ model ApplicationSettings {
|
||||
|
||||
model Secret {
|
||||
id String @id @default(cuid())
|
||||
name String @unique
|
||||
name String
|
||||
value String
|
||||
isBuildSecret Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
application Application @relation(fields: [applicationId], references: [id])
|
||||
applicationId String
|
||||
|
||||
@@unique([name, applicationId])
|
||||
}
|
||||
|
||||
model BuildLog {
|
||||
@@ -232,6 +236,7 @@ model Service {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
fqdn String?
|
||||
dualCerts Boolean @default(false)
|
||||
type String?
|
||||
version String?
|
||||
teams Team[]
|
||||
|
||||
105
src/global.d.ts → src/app.d.ts
vendored
105
src/global.d.ts → src/app.d.ts
vendored
@@ -1,74 +1,26 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
interface Cookies {
|
||||
teamId?: string;
|
||||
gitlabToken?: string;
|
||||
'kit.session'?: string;
|
||||
}
|
||||
interface Locals {
|
||||
gitlabToken?: string;
|
||||
user: {
|
||||
teamId: string;
|
||||
permission: string;
|
||||
isAdmin: boolean;
|
||||
};
|
||||
session: {
|
||||
data: {
|
||||
uid?: string;
|
||||
teams?: string[];
|
||||
expires?: string;
|
||||
};
|
||||
};
|
||||
|
||||
declare namespace App {
|
||||
interface Locals {
|
||||
session: import('svelte-kit-cookie-session').Session<SessionData>;
|
||||
cookies: Record<string, string>;
|
||||
}
|
||||
interface Platform {}
|
||||
interface Session extends SessionData {}
|
||||
interface Stuff {}
|
||||
}
|
||||
|
||||
type Applications = {
|
||||
name: string;
|
||||
domain: string;
|
||||
};
|
||||
|
||||
interface Hash {
|
||||
iv: string;
|
||||
content: string;
|
||||
interface SessionData {
|
||||
version?: string;
|
||||
userId?: string | null;
|
||||
teamId?: string | null;
|
||||
permission?: string;
|
||||
isAdmin?: boolean;
|
||||
expires?: string | null;
|
||||
gitlabToken?: string | null;
|
||||
ghToken?: string | null;
|
||||
}
|
||||
|
||||
interface BuildPack {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// TODO: Not used, not working what?!
|
||||
enum GitSource {
|
||||
Github = 'github',
|
||||
Gitlab = 'gitlab',
|
||||
Bitbucket = 'bitbucket'
|
||||
}
|
||||
|
||||
type RawHaproxyConfiguration = {
|
||||
_version: number;
|
||||
data: string;
|
||||
};
|
||||
|
||||
type NewTransaction = {
|
||||
_version: number;
|
||||
id: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
type HttpRequestRuleForceSSL = {
|
||||
return_hdrs: null;
|
||||
cond: string;
|
||||
cond_test: string;
|
||||
index: number;
|
||||
redir_code: number;
|
||||
redir_type: string;
|
||||
redir_value: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
// TODO: No any please
|
||||
type HttpRequestRule = {
|
||||
_version: number;
|
||||
data: Array<any>;
|
||||
};
|
||||
|
||||
type DateTimeFormatOptions = {
|
||||
localeMatcher?: 'lookup' | 'best fit';
|
||||
weekday?: 'long' | 'short' | 'narrow';
|
||||
@@ -84,3 +36,24 @@ type DateTimeFormatOptions = {
|
||||
hour12?: boolean;
|
||||
timeZone?: string;
|
||||
};
|
||||
|
||||
interface Hash {
|
||||
iv: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
type RawHaproxyConfiguration = {
|
||||
_version: number;
|
||||
data: string;
|
||||
};
|
||||
|
||||
type NewTransaction = {
|
||||
_version: number;
|
||||
id: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
type Application = {
|
||||
name: string;
|
||||
domain: string;
|
||||
};
|
||||
@@ -4,9 +4,6 @@
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="dns-prefetch" href="https://cdn.coollabs.io/" />
|
||||
<link rel="preconnect" href="https://cdn.coollabs.io/" crossorigin="" />
|
||||
<link rel="stylesheet" href="https://cdn.coollabs.io/fonts/poppins/poppins.css" />
|
||||
<title>Coolify</title>
|
||||
%svelte.head%
|
||||
</head>
|
||||
|
||||
47
src/hooks.ts
47
src/hooks.ts
@@ -2,7 +2,7 @@ import dotEnvExtended from 'dotenv-extended';
|
||||
dotEnvExtended.load();
|
||||
import type { GetSession } from '@sveltejs/kit';
|
||||
import { handleSession } from 'svelte-kit-cookie-session';
|
||||
import { getUserDetails, isTeamIdTokenAvailable, sentry } from '$lib/common';
|
||||
import { getUserDetails, sentry } from '$lib/common';
|
||||
import { version } from '$lib/common';
|
||||
import cookie from 'cookie';
|
||||
import { dev } from '$app/env';
|
||||
@@ -10,27 +10,38 @@ import { dev } from '$app/env';
|
||||
export const handle = handleSession(
|
||||
{
|
||||
secret: process.env['COOLIFY_SECRET_KEY'],
|
||||
expires: 30
|
||||
expires: 30,
|
||||
cookie: { secure: false }
|
||||
},
|
||||
async function ({ event, resolve }) {
|
||||
let response;
|
||||
try {
|
||||
const cookies: Cookies = cookie.parse(event.request.headers.get('cookie') || '');
|
||||
if (cookies['kit.session']) {
|
||||
const { permission, teamId } = await getUserDetails(event, false);
|
||||
event.locals.user = {
|
||||
teamId,
|
||||
permission,
|
||||
isAdmin: permission === 'admin' || permission === 'owner'
|
||||
};
|
||||
}
|
||||
if (cookies.gitlabToken) {
|
||||
event.locals.gitlabToken = cookies.gitlabToken;
|
||||
if (event.locals.cookies) {
|
||||
let gitlabToken = event.locals.cookies.gitlabToken || null;
|
||||
let ghToken = event.locals.cookies.ghToken;
|
||||
if (event.locals.cookies['kit.session']) {
|
||||
const { permission, teamId, userId } = await getUserDetails(event, false);
|
||||
const newSession = {
|
||||
userId,
|
||||
teamId,
|
||||
permission,
|
||||
isAdmin: permission === 'admin' || permission === 'owner',
|
||||
expires: event.locals.session.data.expires,
|
||||
gitlabToken,
|
||||
ghToken
|
||||
};
|
||||
|
||||
if (JSON.stringify(event.locals.session.data) !== JSON.stringify(newSession)) {
|
||||
event.locals.session.data = { ...newSession };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = await resolve(event, {
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success')
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response = await resolve(event, {
|
||||
ssr: !event.url.pathname.startsWith('/webhooks/success')
|
||||
});
|
||||
@@ -61,17 +72,13 @@ export const handle = handleSession(
|
||||
}
|
||||
);
|
||||
|
||||
export const getSession: GetSession = function (request) {
|
||||
export const getSession: GetSession = function ({ locals }) {
|
||||
return {
|
||||
version,
|
||||
gitlabToken: request.locals?.gitlabToken || null,
|
||||
uid: request.locals.session.data?.uid || null,
|
||||
teamId: request.locals.user?.teamId || null,
|
||||
permission: request.locals.user?.permission,
|
||||
isAdmin: request.locals.user?.isAdmin || false
|
||||
...locals.session.data
|
||||
};
|
||||
};
|
||||
|
||||
export async function handleError({ error, event }) {
|
||||
if (!dev) sentry.captureException(error, { event });
|
||||
if (!dev) sentry.captureException(error, event);
|
||||
}
|
||||
|
||||
@@ -74,26 +74,12 @@ export async function makeLabelForStandaloneDatabase({ id, image, volume }) {
|
||||
];
|
||||
}
|
||||
|
||||
export async function makeLabelForPlausibleAnalytics({ id, images, volume }) {
|
||||
const service = await db.prisma.service.findFirst({
|
||||
where: { id },
|
||||
include: { plausibleAnalytics: true }
|
||||
});
|
||||
delete service.destinationDockerId;
|
||||
delete service.createdAt;
|
||||
delete service.updatedAt;
|
||||
export function makeLabelForServices(type) {
|
||||
return [
|
||||
'coolify.managed=true',
|
||||
`coolify.version=${version}`,
|
||||
`coolify.type=service-plausibleanalytics`,
|
||||
`coolify.configuration=${base64Encode(
|
||||
JSON.stringify({
|
||||
version,
|
||||
images,
|
||||
volume,
|
||||
...service
|
||||
})
|
||||
)}`
|
||||
`coolify.type=service`,
|
||||
`coolify.service.type=${type}`
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`COPY ./${baseDirectory || ''} /var/www/html`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push('CMD ["apache2-foreground"]');
|
||||
Dockerfile.push('RUN chown -R www-data /var/www/html');
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
|
||||
};
|
||||
|
||||
|
||||
@@ -8,16 +8,26 @@ import * as db from '$lib/database';
|
||||
import { buildLogQueue } from './queues';
|
||||
|
||||
import { version as currentVersion } from '../../package.json';
|
||||
import { dockerInstance } from './docker';
|
||||
import dayjs from 'dayjs';
|
||||
import Cookie from 'cookie';
|
||||
import os from 'os';
|
||||
|
||||
try {
|
||||
if (!dev) {
|
||||
Sentry.init({
|
||||
dsn: process.env['COOLIFY_SENTRY_DSN'],
|
||||
tracesSampleRate: 0,
|
||||
environment: 'production'
|
||||
environment: 'production',
|
||||
debug: true,
|
||||
release: currentVersion,
|
||||
initialScope: {
|
||||
tags: {
|
||||
appId: process.env['COOLIFY_APP_ID'],
|
||||
'os.arch': os.arch(),
|
||||
'os.platform': os.platform(),
|
||||
'os.release': os.release()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -57,7 +67,7 @@ export const isTeamIdTokenAvailable = (request) => {
|
||||
};
|
||||
|
||||
export const getTeam = (event) => {
|
||||
const cookies: Cookies = Cookie.parse(event.request.headers.get('cookie'));
|
||||
const cookies = Cookie.parse(event.request.headers.get('cookie'));
|
||||
if (cookies.teamId) {
|
||||
return cookies.teamId;
|
||||
} else if (event.locals.session.data.teamId) {
|
||||
@@ -67,9 +77,8 @@ export const getTeam = (event) => {
|
||||
};
|
||||
|
||||
export const getUserDetails = async (event, isAdminRequired = true) => {
|
||||
// try {
|
||||
const teamId = getTeam(event);
|
||||
const userId = event.locals.session.data.uid || null;
|
||||
const userId = event.locals.session.data.userId || null;
|
||||
const { permission = 'read' } = await db.prisma.permission.findFirst({
|
||||
where: { teamId, userId },
|
||||
select: { permission: true },
|
||||
@@ -91,18 +100,6 @@ export const getUserDetails = async (event, isAdminRequired = true) => {
|
||||
}
|
||||
|
||||
return payload;
|
||||
// } catch (err) {
|
||||
// console.log(err);
|
||||
// return {
|
||||
// teamId: null,
|
||||
// userId: null,
|
||||
// permission: 'read',
|
||||
// status: 401,
|
||||
// body: {
|
||||
// message: 'You do not have permission to do this. \nAsk an admin to modify your permissions.'
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
};
|
||||
|
||||
export function getEngine(engine) {
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
function showActions(value) {
|
||||
actionsShow = value;
|
||||
// if (value === false) {
|
||||
// showPassword = false;
|
||||
// }
|
||||
}
|
||||
function copyToClipboard() {
|
||||
if (isHttps && navigator.clipboard) {
|
||||
@@ -32,7 +29,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<span
|
||||
<div
|
||||
class="relative"
|
||||
on:mouseenter={() => showActions(true)}
|
||||
on:mouseleave={() => showActions(false)}
|
||||
@@ -81,7 +78,7 @@
|
||||
{/if}
|
||||
|
||||
{#if actionsShow}
|
||||
<div class="absolute top-0 right-0 mx-2 cursor-pointer text-warmGray-600 hover:text-white">
|
||||
<div class="absolute top-0 right-0 m-3 cursor-pointer text-warmGray-600 hover:text-white">
|
||||
<div class="flex space-x-2">
|
||||
{#if isPasswordField}
|
||||
<div on:click={() => (showPassword = !showPassword)}>
|
||||
@@ -145,4 +142,4 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
export let text;
|
||||
export let maxWidthClass = 'max-w-[24rem]';
|
||||
export let customClass = 'max-w-[24rem]';
|
||||
</script>
|
||||
|
||||
<div class="py-1 text-xs text-stone-400 {maxWidthClass}">{@html text}</div>
|
||||
<div class="py-1 text-xs text-stone-400 {customClass}">{@html text}</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<span class="loader" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class=" main h-64 py-24 left-0 top-0 flex flex-wrap content-center mx-auto">
|
||||
<div class="main h-64 py-24 left-0 top-0 flex flex-wrap content-center mx-auto">
|
||||
<span class="loader" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -4,21 +4,18 @@
|
||||
export let setting;
|
||||
export let title;
|
||||
export let description;
|
||||
export let isPadding = true;
|
||||
export let isCenter = true;
|
||||
export let disabled = false;
|
||||
// export let disabledReason = '';
|
||||
export let dataTooltip = null;
|
||||
</script>
|
||||
|
||||
<li class="flex items-center py-4">
|
||||
<div class="flex w-96 flex-col" class:px-4={isPadding} class:pr-32={!isPadding}>
|
||||
<p class="text-xs font-bold text-stone-100 md:text-base">{title}</p>
|
||||
<div class="flex items-center py-4 pr-8">
|
||||
<div class="flex w-96 flex-col">
|
||||
<div class="text-xs font-bold text-stone-100 md:text-base">{title}</div>
|
||||
<Explainer text={description} />
|
||||
</div>
|
||||
<!-- {#if disabled}
|
||||
<div class="flex" class:px-4={isPadding} class:pr-32={!isPadding}>
|
||||
<p class="text-xs font-bold text-stone-400">{disabledReason}</p>
|
||||
</div>
|
||||
{:else} -->
|
||||
</div>
|
||||
<div class:tooltip={dataTooltip} class:text-center={isCenter} data-tooltip={dataTooltip}>
|
||||
<div
|
||||
type="button"
|
||||
on:click
|
||||
@@ -64,5 +61,4 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- {/if} -->
|
||||
</li>
|
||||
</div>
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
<img
|
||||
alt="minio logo"
|
||||
class={isAbsolute ? 'w-7 absolute top-0 left-0 -m-3 -mt-5' : 'w-4 mx-auto'}
|
||||
src="https://cdn.coollabs.io/assets/coolify/services/minio/MINIO_Bird.png"
|
||||
src="/minio.png"
|
||||
/>
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
<img
|
||||
alt="nocodb logo"
|
||||
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
|
||||
src="https://cdn.coollabs.io/assets/coolify/services/nocodb/nocodb.png"
|
||||
src="/nocodb.png"
|
||||
/>
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
<img
|
||||
alt="plausible logo"
|
||||
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-6 mx-auto'}
|
||||
src="https://cdn.coollabs.io/assets/coolify/services/plausible/logo_sm.png"
|
||||
src="/plausible.png"
|
||||
/>
|
||||
|
||||
35
src/lib/components/svg/services/VaultWarden.svelte
Normal file
35
src/lib/components/svg/services/VaultWarden.svelte
Normal file
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
id="Icon"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 1024 1024"
|
||||
style="enable-background:new 0 0 1024 1024;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<style type="text/css">
|
||||
.st0 {
|
||||
fill: #175ddc;
|
||||
}
|
||||
.st1 {
|
||||
fill: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<path
|
||||
id="Background"
|
||||
class="st0"
|
||||
d="M1024,864c0,88.4-71.6,160-160,160H160C71.6,1024,0,952.4,0,864V160C0,71.6,71.6,0,160,0h704 c88.4,0,160,71.6,160,160V864z"
|
||||
/>
|
||||
<path
|
||||
id="Identity"
|
||||
class="st1"
|
||||
d="M829.8,128.6c-6.5-6.5-14.2-9.7-23-9.7H217.2c-8.9,0-16.5,3.2-23,9.7c-6.5,6.5-9.7,14.2-9.7,23 v393.1c0,29.3,5.7,58.4,17.1,87.3c11.4,28.8,25.6,54.4,42.5,76.8c16.9,22.3,37,44.1,60.4,65.3c23.4,21.2,45,38.7,64.7,52.7 c19.8,14,40.4,27.2,61.9,39.7c21.5,12.5,36.8,20.9,45.8,25.3c9,4.4,16.3,7.9,21.7,10.2c4.1,2,8.5,3.1,13.3,3.1c4.8,0,9.2-1,13.3-3.1 c5.5-2.4,12.7-5.8,21.8-10.2c9-4.4,24.3-12.9,45.8-25.3c21.5-12.5,42.1-25.7,61.9-39.7c19.8-14,41.4-31.6,64.8-52.7 c23.4-21.2,43.5-42.9,60.4-65.3c16.9-22.4,31-47.9,42.5-76.8c11.4-28.8,17.1-57.9,17.1-87.3V151.7 C839.6,142.8,836.3,135.1,829.8,128.6z M753.8,548.4c0,142.3-241.8,264.9-241.8,264.9V203.1h241.8 C753.8,203.1,753.8,406.1,753.8,548.4z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { removeProxyConfiguration } from '$lib/haproxy';
|
||||
import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
|
||||
import { getDomain, removeDestinationDocker } from '$lib/common';
|
||||
import { prisma } from './common';
|
||||
@@ -59,10 +59,14 @@ export async function removeApplication({ id, teamId }) {
|
||||
const id = containerObj.ID;
|
||||
const preview = containerObj.Image.split('-')[1];
|
||||
await removeDestinationDocker({ id, engine: destinationDocker.engine });
|
||||
if (preview) {
|
||||
await removeProxyConfiguration({ domain: `${preview}.${domain}` });
|
||||
} else {
|
||||
await removeProxyConfiguration({ domain });
|
||||
try {
|
||||
if (preview) {
|
||||
await removeProxyConfiguration({ domain: `${preview}.${domain}` });
|
||||
} else {
|
||||
await removeProxyConfiguration({ domain });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +117,13 @@ export async function getApplicationWebhook({ projectId, branch }) {
|
||||
throw { status: 404, body: { message: e.message } };
|
||||
}
|
||||
}
|
||||
export async function getApplicationById({ id }) {
|
||||
const body = await prisma.application.findFirst({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
return { ...body };
|
||||
}
|
||||
export async function getApplication({ id, teamId }) {
|
||||
let body = await prisma.application.findFirst({
|
||||
where: { id, teams: { some: { id: teamId } } },
|
||||
@@ -198,10 +209,10 @@ export async function configureApplication({
|
||||
});
|
||||
}
|
||||
|
||||
export async function setApplicationSettings({ id, debug, previews }) {
|
||||
export async function setApplicationSettings({ id, debug, previews, dualCerts }) {
|
||||
return await prisma.application.update({
|
||||
where: { id },
|
||||
data: { settings: { update: { debug, previews } } },
|
||||
data: { settings: { update: { debug, previews, dualCerts } } },
|
||||
include: { destinationDocker: true }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getDomain } from '$lib/common';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function isBranchAlreadyUsed({ repository, branch, id }) {
|
||||
const application = await prisma.application.findUnique({
|
||||
@@ -21,16 +21,35 @@ export async function isSecretExists({ id, name }) {
|
||||
|
||||
export async function isDomainConfigured({ id, fqdn }) {
|
||||
const domain = getDomain(fqdn);
|
||||
const nakedDomain = domain.replace('www.', '');
|
||||
const foundApp = await prisma.application.findFirst({
|
||||
where: { fqdn: { endsWith: domain }, id: { not: id } },
|
||||
where: {
|
||||
OR: [
|
||||
{ fqdn: { endsWith: `//${nakedDomain}` } },
|
||||
{ fqdn: { endsWith: `//www.${nakedDomain}` } }
|
||||
],
|
||||
id: { not: id }
|
||||
},
|
||||
select: { fqdn: true }
|
||||
});
|
||||
const foundService = await prisma.service.findFirst({
|
||||
where: { fqdn: { endsWith: domain }, id: { not: id } },
|
||||
where: {
|
||||
OR: [
|
||||
{ fqdn: { endsWith: `//${nakedDomain}` } },
|
||||
{ fqdn: { endsWith: `//www.${nakedDomain}` } }
|
||||
],
|
||||
id: { not: id }
|
||||
},
|
||||
select: { fqdn: true }
|
||||
});
|
||||
const coolifyFqdn = await prisma.setting.findFirst({
|
||||
where: { fqdn: { endsWith: domain }, id: { not: id } },
|
||||
where: {
|
||||
OR: [
|
||||
{ fqdn: { endsWith: `//${nakedDomain}` } },
|
||||
{ fqdn: { endsWith: `//www.${nakedDomain}` } }
|
||||
],
|
||||
id: { not: id }
|
||||
},
|
||||
select: { fqdn: true }
|
||||
});
|
||||
if (foundApp || foundService || coolifyFqdn) return true;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { dev } from '$app/env';
|
||||
import { sentry } from '$lib/common';
|
||||
import * as Prisma from '@prisma/client';
|
||||
import { default as ProdPrisma } from '@prisma/client';
|
||||
import type { PrismaClientOptions } from '@prisma/client/runtime';
|
||||
import generator from 'generate-password';
|
||||
import forge from 'node-forge';
|
||||
|
||||
@@ -19,37 +20,45 @@ if (!dev) {
|
||||
PrismaClient = ProdPrisma.PrismaClient;
|
||||
P = ProdPrisma.Prisma;
|
||||
}
|
||||
let prismaOptions = {
|
||||
rejectOnNotFound: false
|
||||
};
|
||||
if (dev) {
|
||||
prismaOptions = {
|
||||
errorFormat: 'pretty',
|
||||
rejectOnNotFound: false,
|
||||
log: [
|
||||
{
|
||||
emit: 'event',
|
||||
level: 'query'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
export const prisma = new PrismaClient(prismaOptions);
|
||||
|
||||
export function PrismaErrorHandler(e) {
|
||||
sentry.captureException(e);
|
||||
export const prisma = new PrismaClient({
|
||||
errorFormat: 'pretty',
|
||||
rejectOnNotFound: false
|
||||
});
|
||||
|
||||
export function ErrorHandler(e) {
|
||||
if (e! instanceof Error) {
|
||||
e = new Error(e.toString());
|
||||
}
|
||||
let truncatedError = e;
|
||||
if (e.stdout) {
|
||||
truncatedError = e.stdout;
|
||||
}
|
||||
if (e.message?.includes('docker run')) {
|
||||
let truncatedArray = [];
|
||||
truncatedArray = truncatedError.message.split('-').filter((line) => {
|
||||
if (!line.startsWith('e ')) {
|
||||
return line;
|
||||
}
|
||||
});
|
||||
truncatedError.message = truncatedArray.join('-');
|
||||
}
|
||||
if (e.message?.includes('git clone')) {
|
||||
truncatedError.message = 'git clone failed';
|
||||
}
|
||||
sentry.captureException(truncatedError);
|
||||
const payload = {
|
||||
status: e.status || 500,
|
||||
status: truncatedError.status || 500,
|
||||
body: {
|
||||
message: 'Ooops, something is not okay, are you okay?',
|
||||
error: e.error || e.message
|
||||
error: truncatedError.error || truncatedError.message
|
||||
}
|
||||
};
|
||||
if (e.name === 'NotFoundError') {
|
||||
if (truncatedError?.name === 'NotFoundError') {
|
||||
payload.status = 404;
|
||||
}
|
||||
if (e instanceof P.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
if (truncatedError instanceof P.PrismaClientKnownRequestError) {
|
||||
if (truncatedError?.code === 'P2002') {
|
||||
payload.body.message = 'Already exists. Choose another name.';
|
||||
}
|
||||
}
|
||||
@@ -98,21 +107,55 @@ export const supportedServiceTypesAndVersions = [
|
||||
name: 'plausibleanalytics',
|
||||
fancyName: 'Plausible Analytics',
|
||||
baseImage: 'plausible/analytics',
|
||||
versions: ['latest']
|
||||
versions: ['latest'],
|
||||
ports: {
|
||||
main: 8000
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'nocodb',
|
||||
fancyName: 'NocoDB',
|
||||
baseImage: 'nocodb/nocodb',
|
||||
versions: ['latest'],
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'minio',
|
||||
fancyName: 'MinIO',
|
||||
baseImage: 'minio/minio',
|
||||
versions: ['latest'],
|
||||
ports: {
|
||||
main: 9001
|
||||
}
|
||||
},
|
||||
{ name: 'nocodb', fancyName: 'NocoDB', baseImage: 'nocodb/nocodb', versions: ['latest'] },
|
||||
{ name: 'minio', fancyName: 'MinIO', baseImage: 'minio/minio', versions: ['latest'] },
|
||||
{
|
||||
name: 'vscodeserver',
|
||||
fancyName: 'VSCode Server',
|
||||
baseImage: 'codercom/code-server',
|
||||
versions: ['latest']
|
||||
versions: ['latest'],
|
||||
ports: {
|
||||
main: 8080
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'wordpress',
|
||||
fancyName: 'Wordpress',
|
||||
baseImage: 'wordpress',
|
||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3']
|
||||
versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'],
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'vaultwarden',
|
||||
fancyName: 'Vaultwarden',
|
||||
baseImage: 'vaultwarden/server',
|
||||
versions: ['latest'],
|
||||
ports: {
|
||||
main: 80
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import cuid from 'cuid';
|
||||
import { generatePassword } from '.';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma, ErrorHandler } from './common';
|
||||
import getPort from 'get-port';
|
||||
import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
|
||||
|
||||
@@ -114,15 +114,6 @@ export async function updateDatabase({
|
||||
});
|
||||
}
|
||||
|
||||
// export async function setDatabaseSettings({ id, isPublic }) {
|
||||
// try {
|
||||
// await prisma.databaseSettings.update({ where: { databaseId: id }, data: { isPublic } })
|
||||
// return { status: 201 }
|
||||
// } catch (e) {
|
||||
// throw PrismaErrorHandler(e)
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function stopDatabase(database) {
|
||||
let everStarted = false;
|
||||
const {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { defaultProxyImageHttp, defaultProxyImageTcp, startCoolifyProxy } from '$lib/haproxy';
|
||||
import { startCoolifyProxy } from '$lib/haproxy';
|
||||
import { getDatabaseImage } from '.';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function listDestinations(teamId) {
|
||||
return await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId } } } });
|
||||
@@ -37,11 +37,9 @@ export async function configureDestinationForDatabase({ id, destinationId }) {
|
||||
const host = getEngine(engine);
|
||||
if (type && version) {
|
||||
const baseImage = getDatabaseImage(type);
|
||||
asyncExecShell(`DOCKER_HOST=${host} docker pull ${baseImage}:${version}`);
|
||||
asyncExecShell(`DOCKER_HOST=${host} docker pull coollabsio/${defaultProxyImageTcp}`);
|
||||
asyncExecShell(`DOCKER_HOST=${host} docker pull coollabsio/${defaultProxyImageHttp}`);
|
||||
asyncExecShell(`DOCKER_HOST=${host} docker pull certbot/certbot:latest`);
|
||||
asyncExecShell(`DOCKER_HOST=${host} docker pull alpine:latest`);
|
||||
asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker pull ${baseImage}:${version} && echo "FROM ${baseImage}:${version}" | docker build --label coolify.image="true" -t "${baseImage}:${version}" -`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,22 +109,4 @@ export async function setDestinationSettings({ engine, isCoolifyProxyUsed }) {
|
||||
where: { engine },
|
||||
data: { isCoolifyProxyUsed }
|
||||
});
|
||||
|
||||
// if (isCoolifyProxyUsed) {
|
||||
// await installCoolifyProxy(engine)
|
||||
// await configureNetworkCoolifyProxy(engine)
|
||||
// } else {
|
||||
// // TODO: must check if other destination is using the proxy??? or not?
|
||||
// const domain = await prisma.setting.findUnique({ where: { name: 'domain' }, rejectOnNotFound: false })
|
||||
// if (!domain) {
|
||||
// await uninstallCoolifyProxy(engine)
|
||||
// } else {
|
||||
// return {
|
||||
// stastus: 500,
|
||||
// body: {
|
||||
// message: 'You can not disable the Coolify proxy while the domain is set for Coolify itself.'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function listSources(teamId) {
|
||||
return await prisma.gitSource.findMany({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function addInstallation({ gitSourceId, installation_id }) {
|
||||
const source = await prisma.gitSource.findUnique({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { encrypt } from '$lib/crypto';
|
||||
import { generateSshKeyPair, prisma, PrismaErrorHandler } from './common';
|
||||
import { generateSshKeyPair, prisma } from './common';
|
||||
|
||||
export async function updateDeployKey({ id, deployKeyId }) {
|
||||
const application = await prisma.application.findUnique({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma, ErrorHandler } from './common';
|
||||
|
||||
export async function listLogs({ buildId, last = 0 }) {
|
||||
try {
|
||||
@@ -7,7 +7,7 @@ export async function listLogs({ buildId, last = 0 }) {
|
||||
orderBy: { time: 'asc' }
|
||||
});
|
||||
return [...body];
|
||||
} catch (e) {
|
||||
throw PrismaErrorHandler(e);
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { encrypt } from '$lib/crypto';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function listSecrets({ applicationId }) {
|
||||
return await prisma.secret.findMany({
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import cuid from 'cuid';
|
||||
import { generatePassword } from '.';
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function listServices(teamId) {
|
||||
return await prisma.service.findMany({ where: { teams: { some: { id: teamId } } } });
|
||||
@@ -99,15 +98,29 @@ export async function configureServiceType({ id, type }) {
|
||||
wordpress: { create: { mysqlPassword, mysqlRootUserPassword, mysqlRootUser, mysqlUser } }
|
||||
}
|
||||
});
|
||||
} else if (type === 'vaultwarden') {
|
||||
await prisma.service.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
export async function setService({ id, version }) {
|
||||
export async function setServiceVersion({ id, version }) {
|
||||
return await prisma.service.update({
|
||||
where: { id },
|
||||
data: { version }
|
||||
});
|
||||
}
|
||||
|
||||
export async function setServiceSettings({ id, dualCerts }) {
|
||||
return await prisma.service.update({
|
||||
where: { id },
|
||||
data: { dualCerts }
|
||||
});
|
||||
}
|
||||
|
||||
export async function updatePlausibleAnalyticsService({ id, fqdn, email, username, name }) {
|
||||
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
|
||||
await prisma.service.update({ where: { id }, data: { name, fqdn } });
|
||||
@@ -115,6 +128,9 @@ export async function updatePlausibleAnalyticsService({ id, fqdn, email, usernam
|
||||
export async function updateNocoDbOrMinioService({ id, fqdn, name }) {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
}
|
||||
export async function updateVaultWardenService({ id, fqdn, name }) {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
}
|
||||
export async function updateVsCodeServer({ id, fqdn, name }) {
|
||||
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { prisma } from './common';
|
||||
|
||||
export async function listTeams() {
|
||||
return await prisma.team.findMany();
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import cuid from 'cuid';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
import { prisma, PrismaErrorHandler } from './common';
|
||||
import { asyncExecShell, removeContainer, uniqueName } from '$lib/common';
|
||||
import { prisma } from './common';
|
||||
import { asyncExecShell, uniqueName } from '$lib/common';
|
||||
|
||||
import * as db from '$lib/database';
|
||||
import { startCoolifyProxy } from '$lib/haproxy';
|
||||
|
||||
export async function login({ email, password }) {
|
||||
export async function hashPassword(password: string) {
|
||||
const saltRounds = 15;
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
export async function login({ email, password }) {
|
||||
const users = await prisma.user.count();
|
||||
const userFound = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
include: { teams: true },
|
||||
include: { teams: true, permission: true },
|
||||
rejectOnNotFound: false
|
||||
});
|
||||
// Registration disabled if database is not seeded properly
|
||||
const { isRegistrationEnabled, id } = await db.listSettings();
|
||||
|
||||
let uid = cuid();
|
||||
let permission = 'read';
|
||||
let isAdmin = false;
|
||||
// Disable registration if we are registering the first user.
|
||||
if (users === 0) {
|
||||
await prisma.setting.update({ where: { id }, data: { isRegistrationEnabled: false } });
|
||||
@@ -28,12 +32,12 @@ export async function login({ email, password }) {
|
||||
console.log('Network created');
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Network already exists');
|
||||
console.log('Network already exists.');
|
||||
});
|
||||
|
||||
startCoolifyProxy('/var/run/docker.sock')
|
||||
.then(() => {
|
||||
console.log('Coolify Proxy Started');
|
||||
console.log('Coolify Proxy started.');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
@@ -50,6 +54,8 @@ export async function login({ email, password }) {
|
||||
};
|
||||
}
|
||||
uid = userFound.id;
|
||||
// permission = userFound.permission;
|
||||
isAdmin = true;
|
||||
}
|
||||
} else {
|
||||
// If registration disabled, return 403
|
||||
@@ -59,8 +65,10 @@ export async function login({ email, password }) {
|
||||
};
|
||||
}
|
||||
|
||||
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||
const hashedPassword = await hashPassword(password);
|
||||
if (users === 0) {
|
||||
permission = 'owner';
|
||||
isAdmin = true;
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
id: uid,
|
||||
@@ -97,25 +105,16 @@ export async function login({ email, password }) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// const token = jsonwebtoken.sign({}, secretKey, {
|
||||
// expiresIn: 15778800,
|
||||
// algorithm: 'HS256',
|
||||
// audience: 'coolify',
|
||||
// issuer: 'coolify',
|
||||
// jwtid: uid,
|
||||
// subject: `User:${uid}`,
|
||||
// notBefore: -1000
|
||||
// });
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Set-Cookie': `teamId=${uid}; HttpOnly; Path=/; Max-Age=15778800;`
|
||||
},
|
||||
body: {
|
||||
uid,
|
||||
teamId: uid
|
||||
userId: uid,
|
||||
teamId: uid,
|
||||
permission,
|
||||
isAdmin
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { dev } from '$app/env';
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import got from 'got';
|
||||
import * as db from '$lib/database';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
|
||||
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
|
||||
|
||||
@@ -49,13 +48,14 @@ export async function completeTransaction(transactionId) {
|
||||
return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`);
|
||||
}
|
||||
|
||||
export async function removeProxyConfiguration({ domain }) {
|
||||
export async function removeProxyConfiguration(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const haproxy = await haproxyInstance();
|
||||
const transactionId = await getNextTransactionId();
|
||||
const backendFound = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/backends/${domain}`)
|
||||
.json();
|
||||
if (backendFound) {
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/backends/${domain}`, {
|
||||
searchParams: {
|
||||
@@ -65,104 +65,102 @@ export async function removeProxyConfiguration({ domain }) {
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
await forceSSLOffApplication(domain);
|
||||
await removeWwwRedirection(fqdn);
|
||||
}
|
||||
export async function forceSSLOffApplication({ domain }) {
|
||||
if (!dev) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
const transactionId = await getNextTransactionId();
|
||||
try {
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) => rule.cond_test.includes(`-i ${domain}`));
|
||||
if (rule) {
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
export async function forceSSLOffApplication(domain) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let transactionId;
|
||||
try {
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) =>
|
||||
rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`)
|
||||
);
|
||||
if (rule) {
|
||||
transactionId = await getNextTransactionId();
|
||||
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
} else {
|
||||
console.log(`[DEBUG] Removing ssl for ${domain}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
if (transactionId) await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
export async function forceSSLOnApplication({ domain }) {
|
||||
if (!dev) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
const transactionId = await getNextTransactionId();
|
||||
|
||||
try {
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
let nextRule = 0;
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) => rule.cond_test.includes(`-i ${domain}`));
|
||||
if (rule) return;
|
||||
nextRule = rules.data[rules.data.length - 1].index + 1;
|
||||
}
|
||||
await haproxy
|
||||
.post(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
},
|
||||
json: {
|
||||
index: nextRule,
|
||||
cond: 'if',
|
||||
cond_test: `{ hdr(Host) -i ${domain} } !{ ssl_fc }`,
|
||||
type: 'redirect',
|
||||
redir_type: 'scheme',
|
||||
redir_value: 'https',
|
||||
redir_code: 301
|
||||
}
|
||||
})
|
||||
.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
} finally {
|
||||
await completeTransaction(transactionId);
|
||||
export async function forceSSLOnApplication(domain) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let transactionId;
|
||||
try {
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
let nextRule = 0;
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) =>
|
||||
rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`)
|
||||
);
|
||||
if (rule) return;
|
||||
nextRule = rules.data[rules.data.length - 1].index + 1;
|
||||
}
|
||||
} else {
|
||||
console.log(`[DEBUG] Adding ssl for ${domain}`);
|
||||
transactionId = await getNextTransactionId();
|
||||
|
||||
await haproxy
|
||||
.post(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
},
|
||||
json: {
|
||||
index: nextRule,
|
||||
cond: 'if',
|
||||
cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`,
|
||||
type: 'redirect',
|
||||
redir_type: 'scheme',
|
||||
redir_value: 'https',
|
||||
redir_code: dev ? 302 : 301
|
||||
}
|
||||
})
|
||||
.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (transactionId) await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteProxy({ id }) {
|
||||
const haproxy = await haproxyInstance();
|
||||
try {
|
||||
await checkHAProxy(haproxy);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
const transactionId = await getNextTransactionId();
|
||||
await checkHAProxy(haproxy);
|
||||
let transactionId;
|
||||
try {
|
||||
await haproxy.get(`v2/services/haproxy/configuration/backends/${id}`).json();
|
||||
transactionId = await getNextTransactionId();
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/backends/${id}`, {
|
||||
searchParams: {
|
||||
@@ -181,97 +179,10 @@ export async function deleteProxy({ id }) {
|
||||
} catch (error) {
|
||||
console.log(error.response.body);
|
||||
} finally {
|
||||
await completeTransaction(transactionId);
|
||||
if (transactionId) await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
// export async function configureProxyForDatabase({ id, port, isPublic, privatePort }) {
|
||||
// const haproxy = await haproxyInstance()
|
||||
// try {
|
||||
// await checkHAProxy()
|
||||
// } catch (error) {
|
||||
// return
|
||||
// }
|
||||
|
||||
// let alreadyConfigured = false
|
||||
// try {
|
||||
// const backend: any = await haproxy.get(`v2/services/haproxy/configuration/backends/${id}`).json()
|
||||
// const server: any = await haproxy.get(`v2/services/haproxy/configuration/servers/${id}`, {
|
||||
// searchParams: {
|
||||
// backend: id
|
||||
// },
|
||||
// }).json()
|
||||
// if (backend.data.name === id) {
|
||||
// if (server.data.port === privatePort) {
|
||||
// if (server.data.check === 'enabled') {
|
||||
// if (server.data.address === id) {
|
||||
// alreadyConfigured = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.log('error getting backend or server', error.response.body)
|
||||
// }
|
||||
// if (alreadyConfigured) return
|
||||
|
||||
// const transactionId = await getNextTransactionId()
|
||||
// try {
|
||||
// await haproxy.post('v2/services/haproxy/configuration/backends', {
|
||||
// searchParams: {
|
||||
// transaction_id: transactionId
|
||||
// },
|
||||
// json: {
|
||||
// "init-addr": "last,libc,none",
|
||||
// "mode": "tcp",
|
||||
// "name": id
|
||||
// }
|
||||
// })
|
||||
// await haproxy.post('v2/services/haproxy/configuration/servers', {
|
||||
// searchParams: {
|
||||
// transaction_id: transactionId,
|
||||
// backend: id
|
||||
// },
|
||||
// json: {
|
||||
// "address": id,
|
||||
// "check": "enabled",
|
||||
// "name": id,
|
||||
// "port": privatePort
|
||||
// }
|
||||
// })
|
||||
// await haproxy.post('v2/services/haproxy/configuration/frontends', {
|
||||
// searchParams: {
|
||||
// transaction_id: transactionId,
|
||||
// backend: id
|
||||
// },
|
||||
// json: {
|
||||
// "default_backend": id,
|
||||
// "mode": "tcp",
|
||||
// "name": id
|
||||
// }
|
||||
// })
|
||||
// await haproxy.post('v2/services/haproxy/configuration/binds', {
|
||||
// searchParams: {
|
||||
// transaction_id: transactionId,
|
||||
// frontend: id
|
||||
// },
|
||||
// json: {
|
||||
// "address": "*",
|
||||
// "name": id,
|
||||
// "port": port
|
||||
// }
|
||||
// })
|
||||
// } catch (error) {
|
||||
// console.log(error.response.body)
|
||||
// throw error.response.body
|
||||
// } finally {
|
||||
// try {
|
||||
// await completeTransaction(transactionId)
|
||||
// } catch (error) {
|
||||
// console.log(error.response.body)
|
||||
// }
|
||||
// }
|
||||
// await configureDatabaseVisibility({ id, isPublic })
|
||||
// }
|
||||
export async function reloadHaproxy(engine) {
|
||||
const host = getEngine(engine);
|
||||
return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`);
|
||||
@@ -300,7 +211,7 @@ export async function configureProxyForApplication({ domain, imageId, applicatio
|
||||
if (backendAvailable.data.forwardfor.enabled === 'enabled') {
|
||||
if (backendAvailable.data.name === domain) {
|
||||
if (server.data.check === 'enabled') {
|
||||
if (server.data.address === applicationId) {
|
||||
if (server.data.address === imageId) {
|
||||
if (server.data.port === port) {
|
||||
serverConfigured = true;
|
||||
}
|
||||
@@ -356,13 +267,15 @@ export async function configureProxyForApplication({ domain, imageId, applicatio
|
||||
}
|
||||
}
|
||||
|
||||
export async function configureCoolifyProxyOff({ domain }) {
|
||||
export async function configureCoolifyProxyOff(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
|
||||
try {
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json();
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/backends/${domain}`, {
|
||||
searchParams: {
|
||||
@@ -371,22 +284,25 @@ export async function configureCoolifyProxyOff({ domain }) {
|
||||
})
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
if (!dev) {
|
||||
await forceSSLOffApplication({ domain });
|
||||
}
|
||||
if (isHttps) await forceSSLOffApplication(domain);
|
||||
await removeWwwRedirection(fqdn);
|
||||
} catch (error) {
|
||||
throw error?.response?.body || error;
|
||||
}
|
||||
}
|
||||
export async function checkHAProxy(haproxy) {
|
||||
export async function checkHAProxy(haproxy?: any) {
|
||||
if (!haproxy) haproxy = await haproxyInstance();
|
||||
try {
|
||||
await haproxy.get('v2/info');
|
||||
} catch (error) {
|
||||
throw 'HAProxy is not running, but it should be!';
|
||||
throw {
|
||||
message:
|
||||
'Coolify Proxy is not running, but it should be!<br><br>Start it in the "Destinations" menu.'
|
||||
};
|
||||
}
|
||||
}
|
||||
export async function configureCoolifyProxyOn({ domain }) {
|
||||
export async function configureCoolifyProxyOn(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let serverConfigured = false;
|
||||
@@ -481,8 +397,12 @@ export async function startTcpProxy(destinationDocker, id, publicPort, privatePo
|
||||
|
||||
try {
|
||||
if (foundDB && !found) {
|
||||
const { stdout: Config } = await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
||||
);
|
||||
const ip = JSON.parse(Config)[0].Gateway;
|
||||
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' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} -d coollabsio/${defaultProxyImageTcp}`
|
||||
`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}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -499,8 +419,12 @@ export async function startHttpProxy(destinationDocker, id, publicPort, privateP
|
||||
|
||||
try {
|
||||
if (foundDB && !found) {
|
||||
const { stdout: Config } = await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
||||
);
|
||||
const ip = JSON.parse(Config)[0].Gateway;
|
||||
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' --network ${network} -p ${publicPort}:${publicPort} --name ${containerName} -d coollabsio/${defaultProxyImageHttp}`
|
||||
`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/${defaultProxyImageHttp}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -512,8 +436,12 @@ export async function startCoolifyProxy(engine) {
|
||||
const found = await checkContainer(engine, 'coolify-haproxy');
|
||||
const { 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 -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restart always --add-host 'host.docker.internal:host-gateway' -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 configureNetworkCoolifyProxy(engine);
|
||||
@@ -574,53 +502,70 @@ export async function configureNetworkCoolifyProxy(engine) {
|
||||
|
||||
export async function configureSimpleServiceProxyOn({ id, domain, port }) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let serverConfigured = false;
|
||||
let backendAvailable: any = null;
|
||||
|
||||
try {
|
||||
await checkHAProxy(haproxy);
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json();
|
||||
return;
|
||||
backendAvailable = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/backends/${domain}`)
|
||||
.json();
|
||||
const server: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/servers/${id}`, {
|
||||
searchParams: {
|
||||
backend: domain
|
||||
}
|
||||
})
|
||||
.json();
|
||||
if (backendAvailable && server) {
|
||||
// Very sophisticated way to check if the server is already configured in proxy
|
||||
if (backendAvailable.data.forwardfor.enabled === 'enabled') {
|
||||
if (backendAvailable.data.name === domain) {
|
||||
if (server.data.check === 'enabled') {
|
||||
if (server.data.address === id) {
|
||||
if (server.data.port === port) {
|
||||
serverConfigured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
try {
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy.post('v2/services/haproxy/configuration/backends', {
|
||||
searchParams: {
|
||||
transaction_id: transactionId
|
||||
},
|
||||
json: {
|
||||
'init-addr': 'last,libc,none',
|
||||
forwardfor: { enabled: 'enabled' },
|
||||
name: domain
|
||||
}
|
||||
});
|
||||
await haproxy.post('v2/services/haproxy/configuration/servers', {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
backend: domain
|
||||
},
|
||||
json: {
|
||||
address: id,
|
||||
check: 'enabled',
|
||||
name: id,
|
||||
port: port
|
||||
}
|
||||
});
|
||||
await completeTransaction(transactionId);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
if (serverConfigured) return;
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy.post('v2/services/haproxy/configuration/backends', {
|
||||
searchParams: {
|
||||
transaction_id: transactionId
|
||||
},
|
||||
json: {
|
||||
'init-addr': 'last,libc,none',
|
||||
forwardfor: { enabled: 'enabled' },
|
||||
name: domain
|
||||
}
|
||||
});
|
||||
await haproxy.post('v2/services/haproxy/configuration/servers', {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
backend: domain
|
||||
},
|
||||
json: {
|
||||
address: id,
|
||||
check: 'enabled',
|
||||
name: id,
|
||||
port: port
|
||||
}
|
||||
});
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
|
||||
export async function configureSimpleServiceProxyOff({ domain }) {
|
||||
export async function configureSimpleServiceProxyOff(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
|
||||
try {
|
||||
await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json();
|
||||
const transactionId = await getNextTransactionId();
|
||||
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/backends/${domain}`, {
|
||||
searchParams: {
|
||||
@@ -630,5 +575,92 @@ export async function configureSimpleServiceProxyOff({ domain }) {
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
} catch (error) {}
|
||||
await forceSSLOffApplication(domain);
|
||||
await removeWwwRedirection(fqdn);
|
||||
return;
|
||||
}
|
||||
|
||||
export async function removeWwwRedirection(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy();
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue));
|
||||
if (rule) {
|
||||
const transactionId = await getNextTransactionId();
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function setWwwRedirection(fqdn) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let transactionId;
|
||||
|
||||
try {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`;
|
||||
const rules: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
}
|
||||
})
|
||||
.json();
|
||||
let nextRule = 0;
|
||||
if (rules.data.length > 0) {
|
||||
const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue));
|
||||
if (rule) return;
|
||||
nextRule = rules.data[rules.data.length - 1].index + 1;
|
||||
}
|
||||
|
||||
transactionId = await getNextTransactionId();
|
||||
await haproxy
|
||||
.post(`v2/services/haproxy/configuration/http_request_rules`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId,
|
||||
parent_name: 'http',
|
||||
parent_type: 'frontend'
|
||||
},
|
||||
json: {
|
||||
index: nextRule,
|
||||
cond: 'if',
|
||||
cond_test: contTest,
|
||||
type: 'redirect',
|
||||
redir_type: 'location',
|
||||
redir_value: redirectValue,
|
||||
redir_code: dev ? 302 : 301
|
||||
}
|
||||
})
|
||||
.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (transactionId) await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||
import got from 'got';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
|
||||
export default async function ({
|
||||
applicationId,
|
||||
@@ -45,6 +45,6 @@ export default async function ({
|
||||
const { stdout: commit } = await asyncExecShell(`cd ${workdir}/ && git rev-parse HEAD`);
|
||||
return commit.replace('\n', '');
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { asyncExecShell, saveBuildLog } from '$lib/common';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
|
||||
export default async function ({
|
||||
applicationId,
|
||||
|
||||
@@ -1,48 +1,78 @@
|
||||
import { dev } from '$app/env';
|
||||
import { forceSSLOffApplication, forceSSLOnApplication, getNextTransactionId } from '$lib/haproxy';
|
||||
import { forceSSLOffApplication, forceSSLOnApplication } from '$lib/haproxy';
|
||||
import { asyncExecShell, getEngine } from './common';
|
||||
import * as db from '$lib/database';
|
||||
import cuid from 'cuid';
|
||||
import getPort from 'get-port';
|
||||
|
||||
export async function letsEncrypt({ domain, isCoolify = false, id = null }) {
|
||||
try {
|
||||
if (dev) {
|
||||
return await forceSSLOnApplication({ domain });
|
||||
} else {
|
||||
if (isCoolify) {
|
||||
await asyncExecShell(
|
||||
`docker run --rm --name certbot -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080 -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email`
|
||||
);
|
||||
const nakedDomain = domain.replace('www.', '');
|
||||
const wwwDomain = `www.${nakedDomain}`;
|
||||
const randomCuid = cuid();
|
||||
const randomPort = 9080;
|
||||
|
||||
const { stderr } = await asyncExecShell(
|
||||
`docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem`
|
||||
);
|
||||
if (stderr) throw new Error(stderr);
|
||||
return;
|
||||
let host;
|
||||
let dualCerts = false;
|
||||
if (isCoolify) {
|
||||
const data = await db.prisma.setting.findFirst();
|
||||
dualCerts = data.dualCerts;
|
||||
host = 'unix:///var/run/docker.sock';
|
||||
} else {
|
||||
// Check Application
|
||||
const applicationData = await db.prisma.application.findUnique({
|
||||
where: { id },
|
||||
include: { destinationDocker: true, settings: true }
|
||||
});
|
||||
if (applicationData) {
|
||||
if (applicationData?.destinationDockerId && applicationData?.destinationDocker) {
|
||||
host = getEngine(applicationData.destinationDocker.engine);
|
||||
}
|
||||
if (applicationData?.settings?.dualCerts) {
|
||||
dualCerts = applicationData.settings.dualCerts;
|
||||
}
|
||||
}
|
||||
let data: any = await db.prisma.application.findUnique({
|
||||
// Check Service
|
||||
const serviceData = await db.prisma.service.findUnique({
|
||||
where: { id },
|
||||
include: { destinationDocker: true }
|
||||
});
|
||||
if (!data) {
|
||||
data = await db.prisma.service.findUnique({
|
||||
where: { id },
|
||||
include: { destinationDocker: true }
|
||||
});
|
||||
}
|
||||
// Set SSL with Let's encrypt
|
||||
if (data.destinationDockerId && data.destinationDocker) {
|
||||
const host = getEngine(data.destinationDocker.engine);
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot -p 9080:9080 -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port 9080 -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email`
|
||||
);
|
||||
const { stderr } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name bash -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem`
|
||||
);
|
||||
if (stderr) throw new Error(stderr);
|
||||
await forceSSLOnApplication({ domain });
|
||||
if (serviceData) {
|
||||
if (serviceData?.destinationDockerId && serviceData?.destinationDocker) {
|
||||
host = getEngine(serviceData.destinationDocker.engine);
|
||||
}
|
||||
if (serviceData?.dualCerts) {
|
||||
dualCerts = serviceData.dualCerts;
|
||||
}
|
||||
}
|
||||
}
|
||||
await forceSSLOffApplication(domain);
|
||||
if (dualCerts) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${nakedDomain} -d ${wwwDomain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
dev ? '--test-cert' : ''
|
||||
}`
|
||||
);
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "test -d /etc/letsencrypt/live/${nakedDomain}/ && cat /etc/letsencrypt/live/${nakedDomain}/fullchain.pem /etc/letsencrypt/live/${nakedDomain}/privkey.pem > /app/ssl/${nakedDomain}.pem || cat /etc/letsencrypt/live/${wwwDomain}/fullchain.pem /etc/letsencrypt/live/${wwwDomain}/privkey.pem > /app/ssl/${wwwDomain}.pem"`
|
||||
);
|
||||
} else {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${domain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${
|
||||
dev ? '--test-cert' : ''
|
||||
}`
|
||||
);
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest sh -c "cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem"`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
if (error.code !== 0) {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
if (!isCoolify) {
|
||||
await forceSSLOnApplication(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs/promises';
|
||||
import * as buildpacks from '../buildPacks';
|
||||
import * as importers from '../importers';
|
||||
import { dockerInstance } from '../docker';
|
||||
import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog } from '../common';
|
||||
import { configureProxyForApplication, reloadHaproxy } from '../haproxy';
|
||||
import { configureProxyForApplication, reloadHaproxy, setWwwRedirection } from '../haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { decrypt } from '$lib/crypto';
|
||||
import { sentry } from '$lib/common';
|
||||
import {
|
||||
copyBaseConfigurationFiles,
|
||||
makeLabelForStandaloneApplication,
|
||||
@@ -62,10 +64,6 @@ export default async function (job) {
|
||||
if (destinationDockerId) {
|
||||
destinationType = 'docker';
|
||||
}
|
||||
// Not implemented yet
|
||||
// if (destinationKubernetesId) {
|
||||
// destinationType = 'kubernetes'
|
||||
// }
|
||||
|
||||
if (destinationType === 'docker') {
|
||||
const docker = dockerInstance({ destinationDocker });
|
||||
@@ -207,11 +205,10 @@ export default async function (job) {
|
||||
const envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (!secret.isBuildSecret) {
|
||||
envs.push(`--env ${secret.name}=${secret.value}`);
|
||||
}
|
||||
envs.push(`${secret.name}=${secret.value}`);
|
||||
});
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
const labels = makeLabelForStandaloneApplication({
|
||||
applicationId,
|
||||
fqdn,
|
||||
@@ -230,27 +227,41 @@ export default async function (job) {
|
||||
baseDirectory,
|
||||
publishDirectory
|
||||
});
|
||||
saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
||||
const { stderr } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run ${envs.join()} ${labels.join(
|
||||
' '
|
||||
)} --name ${imageId} --network ${docker.network} --restart always -d ${applicationId}:${tag}`
|
||||
);
|
||||
if (stderr) console.log(stderr);
|
||||
saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
||||
|
||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||
saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
|
||||
await configureProxyForApplication({ domain, imageId, applicationId, port });
|
||||
if (isHttps) await letsEncrypt({ domain, id: applicationId });
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
saveBuildLog({ line: 'Proxy configuration successful!', buildId, applicationId });
|
||||
} else {
|
||||
saveBuildLog({
|
||||
line: 'Coolify Proxy is not configured for this destination. Nothing else to do.',
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
try {
|
||||
saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
||||
const { stderr } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --env-file=${workdir}/.env ${labels.join(
|
||||
' '
|
||||
)} --name ${imageId} --network ${
|
||||
docker.network
|
||||
} --restart always -d ${applicationId}:${tag}`
|
||||
);
|
||||
if (stderr) console.log(stderr);
|
||||
saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
|
||||
} catch (error) {
|
||||
saveBuildLog({ line: error, buildId, applicationId });
|
||||
sentry.captureException(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
try {
|
||||
if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
|
||||
saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
|
||||
await configureProxyForApplication({ domain, imageId, applicationId, port });
|
||||
if (isHttps) await letsEncrypt({ domain, id: applicationId });
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
saveBuildLog({ line: 'Proxy configuration successful!', buildId, applicationId });
|
||||
} else {
|
||||
saveBuildLog({
|
||||
line: 'Coolify Proxy is not configured for this destination. Nothing else to do.',
|
||||
buildId,
|
||||
applicationId
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
saveBuildLog({ line: error.stdout || error, buildId, applicationId });
|
||||
sentry.captureException(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,49 @@
|
||||
import { dev } from '$app/env';
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
import { prisma } from '$lib/database';
|
||||
import { defaultProxyImageHttp, defaultProxyImageTcp } from '$lib/haproxy';
|
||||
|
||||
export default async function () {
|
||||
if (!dev) {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||
for (const destinationDocker of destinationDockers) {
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
// Tagging images with labels
|
||||
try {
|
||||
const images = [
|
||||
`coollabsio/${defaultProxyImageTcp}`,
|
||||
`coollabsio/${defaultProxyImageHttp}`,
|
||||
'certbot/certbot:latest',
|
||||
'node:16.14.0-alpine',
|
||||
'alpine:latest',
|
||||
'nginx:stable-alpine',
|
||||
'node:lts',
|
||||
'php:apache',
|
||||
'rust:latest'
|
||||
];
|
||||
for (const image of images) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -`
|
||||
);
|
||||
}
|
||||
} catch (error) {}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
|
||||
} catch (error) {
|
||||
//
|
||||
console.log(error);
|
||||
}
|
||||
// Cleanup images that are not managed by coolify
|
||||
try {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f`
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// Cleanup dangling images
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`);
|
||||
} catch (error) {
|
||||
//
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ const cron = async () => {
|
||||
|
||||
await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } });
|
||||
// await queue.ssl.add('ssl', {}, { repeat: { every: 10000 } });
|
||||
await queue.cleanup.add('cleanup', {}, { repeat: { every: 3600000 } });
|
||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } });
|
||||
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
|
||||
const events = {
|
||||
@@ -107,7 +107,7 @@ cron().catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
const buildQueueName = dev ? cuid() : 'build_queue';
|
||||
const buildQueueName = 'build_queue';
|
||||
const buildQueue = new Queue(buildQueueName, connectionOptions);
|
||||
const buildWorker = new Worker(buildQueueName, async (job) => await builder(job), {
|
||||
concurrency: 2,
|
||||
@@ -120,21 +120,26 @@ buildWorker.on('completed', async (job: Bullmq.Job) => {
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
await asyncExecShell(`rm -fr ${job.data.workdir}`);
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}/`;
|
||||
await asyncExecShell(`rm -fr ${workdir}`);
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
||||
console.log(failedReason);
|
||||
try {
|
||||
await prisma.build.update({ where: { id: job.data.build_id }, data: { status: 'failed' } });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
await asyncExecShell(`rm -fr ${job.data.workdir}`);
|
||||
const workdir = `/tmp/build-sources/${job.data.repository}`;
|
||||
await asyncExecShell(`rm -fr ${workdir}`);
|
||||
}
|
||||
saveBuildLog({ line: 'Failed build!', buildId: job.data.build_id, applicationId: job.data.id });
|
||||
saveBuildLog({
|
||||
line: 'Failed to deploy!',
|
||||
buildId: job.data.build_id,
|
||||
applicationId: job.data.id
|
||||
});
|
||||
saveBuildLog({
|
||||
line: `Reason: ${failedReason.toString()}`,
|
||||
buildId: job.data.build_id,
|
||||
@@ -142,29 +147,7 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
|
||||
});
|
||||
});
|
||||
|
||||
// const letsEncryptQueueName = dev ? cuid() : 'letsencrypt_queue'
|
||||
// const letsEncryptQueue = new Queue(letsEncryptQueueName, connectionOptions)
|
||||
|
||||
// const letsEncryptWorker = new Worker(letsEncryptQueueName, async (job) => await letsencrypt(job), {
|
||||
// concurrency: 1,
|
||||
// ...connectionOptions
|
||||
// })
|
||||
// letsEncryptWorker.on('completed', async () => {
|
||||
// // TODO: Save letsencrypt logs as build logs!
|
||||
// console.log('[DEBUG] Lets Encrypt job completed')
|
||||
// })
|
||||
|
||||
// letsEncryptWorker.on('failed', async (job: Job, failedReason: string) => {
|
||||
// try {
|
||||
// await prisma.applicationSettings.updateMany({ where: { applicationId: job.data.id }, data: { forceSSL: false } })
|
||||
// } catch (error) {
|
||||
// console.log(error)
|
||||
// }
|
||||
// console.log('[DEBUG] Lets Encrypt job failed')
|
||||
// console.log(failedReason)
|
||||
// })
|
||||
|
||||
const buildLogQueueName = dev ? cuid() : 'log_queue';
|
||||
const buildLogQueueName = 'log_queue';
|
||||
const buildLogQueue = new Queue(buildLogQueueName, connectionOptions);
|
||||
const buildLogWorker = new Worker(buildLogQueueName, async (job) => await logger(job), {
|
||||
concurrency: 1,
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { getDomain } from '$lib/common';
|
||||
import { prisma } from '$lib/database';
|
||||
import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import {
|
||||
checkContainer,
|
||||
configureCoolifyProxyOn,
|
||||
configureProxyForApplication,
|
||||
configureSimpleServiceProxyOn,
|
||||
forceSSLOnApplication,
|
||||
reloadHaproxy,
|
||||
startCoolifyProxy
|
||||
setWwwRedirection,
|
||||
startCoolifyProxy,
|
||||
startHttpProxy
|
||||
} from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
|
||||
@@ -23,42 +26,82 @@ export default async function () {
|
||||
(container) => container.Labels['coolify.managed']
|
||||
);
|
||||
for (const configuration of configurations) {
|
||||
const parsedConfiguration = JSON.parse(
|
||||
Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString()
|
||||
);
|
||||
if (configuration.Labels['coolify.type'] === 'standalone-application') {
|
||||
const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration;
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
await configureProxyForApplication({
|
||||
domain,
|
||||
imageId: pullmergeRequestId
|
||||
? `${applicationId}-${pullmergeRequestId}`
|
||||
: applicationId,
|
||||
applicationId,
|
||||
port
|
||||
});
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) await forceSSLOnApplication({ domain });
|
||||
if (configuration.Labels['coolify.configuration']) {
|
||||
const parsedConfiguration = JSON.parse(
|
||||
Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString()
|
||||
);
|
||||
if (
|
||||
parsedConfiguration &&
|
||||
configuration.Labels['coolify.type'] === 'standalone-application'
|
||||
) {
|
||||
const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration;
|
||||
if (fqdn) {
|
||||
const found = await getApplicationById({ id: applicationId });
|
||||
if (found) {
|
||||
const domain = getDomain(fqdn);
|
||||
await configureProxyForApplication({
|
||||
domain,
|
||||
imageId: pullmergeRequestId
|
||||
? `${applicationId}-${pullmergeRequestId}`
|
||||
: applicationId,
|
||||
applicationId,
|
||||
port
|
||||
});
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) await forceSSLOnApplication(domain);
|
||||
await setWwwRedirection(fqdn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const container of containers) {
|
||||
const image = container.Image.split(':')[0];
|
||||
const found = supportedServiceTypesAndVersions.find((a) => a.baseImage === image);
|
||||
if (found) {
|
||||
const type = found.name;
|
||||
const mainPort = found.ports.main;
|
||||
const id = container.Names[0].replace('/', '');
|
||||
const service = await db.prisma.service.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
destinationDocker: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true
|
||||
}
|
||||
});
|
||||
const { fqdn } = service;
|
||||
const domain = getDomain(fqdn);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: mainPort });
|
||||
const publicPort = service[type]?.publicPort;
|
||||
if (publicPort) {
|
||||
const containerFound = await checkContainer(
|
||||
destination.engine,
|
||||
`haproxy-for-${publicPort}`
|
||||
);
|
||||
if (!containerFound) {
|
||||
await startHttpProxy(destination, id, publicPort, 9000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const services = await prisma.service.findMany({});
|
||||
// Check Coolify FQDN and configure proxy if needed
|
||||
const { fqdn } = await db.listSettings();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const found = await checkContainer('/var/run/docker.sock', 'coolify-haproxy');
|
||||
if (!found) await startCoolifyProxy('/var/run/docker.sock');
|
||||
await configureCoolifyProxyOn({ domain });
|
||||
await startCoolifyProxy('/var/run/docker.sock');
|
||||
await configureCoolifyProxyOn(fqdn);
|
||||
await setWwwRedirection(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) await forceSSLOnApplication({ domain });
|
||||
if (isHttps) await forceSSLOnApplication(domain);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
} finally {
|
||||
// await reloadHaproxy('/var/run/docker.sock');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export const publicPaths = [
|
||||
'/login',
|
||||
'/reset',
|
||||
'/reset/password',
|
||||
'/webhooks/success',
|
||||
'/webhooks/github',
|
||||
'/webhooks/github/install',
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
import { publicPaths } from '$lib/settings';
|
||||
|
||||
export const load: Load = async ({ fetch, url, params, session }) => {
|
||||
if (!session.uid && !publicPaths.includes(url.pathname)) {
|
||||
export const load: Load = async ({ fetch, url, session }) => {
|
||||
if (!session.userId && !publicPaths.includes(url.pathname)) {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/login'
|
||||
};
|
||||
}
|
||||
if (!session.uid) {
|
||||
if (!session.userId) {
|
||||
return {};
|
||||
}
|
||||
const endpoint = `/teams.json`;
|
||||
@@ -39,8 +39,7 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { asyncSleep } from '$lib/components/common';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { dev } from '$app/env';
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
let isUpdateAvailable = false;
|
||||
let updateStatus = {
|
||||
@@ -48,9 +47,10 @@
|
||||
checking: false,
|
||||
success: null
|
||||
};
|
||||
|
||||
let latestVersion = 'latest';
|
||||
onMount(async () => {
|
||||
if ($session.uid) {
|
||||
if ($session.userId) {
|
||||
const overrideVersion = browser && window.localStorage.getItem('latestVersion');
|
||||
try {
|
||||
await get(`/login.json`);
|
||||
} catch ({ error }) {
|
||||
@@ -62,10 +62,11 @@
|
||||
updateStatus.checking = true;
|
||||
try {
|
||||
const data = await get(`/update.json`);
|
||||
if (data?.isUpdateAvailable) {
|
||||
await post(`/update.json`, { type: 'pull' });
|
||||
if (overrideVersion || data?.isUpdateAvailable) {
|
||||
latestVersion = overrideVersion || data.latestVersion;
|
||||
isUpdateAvailable = overrideVersion ? true : data?.isUpdateAvailable;
|
||||
await post(`/update.json`, { type: 'pull', latestVersion });
|
||||
}
|
||||
isUpdateAvailable = data?.isUpdateAvailable;
|
||||
} catch (error) {
|
||||
} finally {
|
||||
updateStatus.checking = false;
|
||||
@@ -83,7 +84,7 @@
|
||||
}
|
||||
async function switchTeam() {
|
||||
try {
|
||||
await post(`/index.json?from=${$page.url.pathname}`, {
|
||||
await post(`/dashboard.json?from=${$page.url.pathname}`, {
|
||||
cookie: 'teamId',
|
||||
value: selectedTeamId
|
||||
});
|
||||
@@ -95,53 +96,31 @@
|
||||
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
if (!dev) {
|
||||
try {
|
||||
await post(`/update.json`, { type: 'update' });
|
||||
toast.push('Update completed. Waiting for the new version to start...');
|
||||
let reachable = false;
|
||||
let tries = 0;
|
||||
do {
|
||||
await asyncSleep(4000);
|
||||
try {
|
||||
await get(`/undead.json`);
|
||||
reachable = true;
|
||||
} catch (error) {
|
||||
reachable = false;
|
||||
}
|
||||
if (reachable) break;
|
||||
tries++;
|
||||
} while (!reachable || tries < 120);
|
||||
toast.push('New version reachable. Reloading...');
|
||||
updateStatus.loading = false;
|
||||
updateStatus.success = true;
|
||||
await asyncSleep(3000);
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await post(`/update.json`, { type: 'update', latestVersion });
|
||||
toast.push('Update completed.<br>Waiting for the new version to start...');
|
||||
let reachable = false;
|
||||
let tries = 0;
|
||||
do {
|
||||
await asyncSleep(1000);
|
||||
await asyncSleep(4000);
|
||||
try {
|
||||
await get(`/undead.json`);
|
||||
reachable = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
reachable = false;
|
||||
}
|
||||
console.log(reachable);
|
||||
if (reachable) break;
|
||||
tries++;
|
||||
} while (!reachable || tries < 120);
|
||||
toast.push('New version reachable. Reloading...');
|
||||
await asyncSleep(2000);
|
||||
window.location.reload();
|
||||
updateStatus.loading = false;
|
||||
updateStatus.success = true;
|
||||
await asyncSleep(3000);
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -150,7 +129,7 @@
|
||||
<title>Coolify</title>
|
||||
</svelte:head>
|
||||
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
|
||||
{#if $session.uid}
|
||||
{#if $session.userId}
|
||||
<nav class="nav-main">
|
||||
<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
|
||||
<div class="my-4 h-10 w-10"><img src="/favicon.png" alt="coolLabs logo" /></div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
const endpoint = `/applications/${params.id}.json`;
|
||||
const res = await fetch(endpoint);
|
||||
if (res.ok) {
|
||||
const { application, githubToken, ghToken, isRunning, appId } = await res.json();
|
||||
const { application, isRunning, appId } = await res.json();
|
||||
if (!application || Object.entries(application).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -42,8 +42,6 @@
|
||||
},
|
||||
stuff: {
|
||||
isRunning,
|
||||
ghToken,
|
||||
githubToken,
|
||||
application,
|
||||
appId
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { asyncExecShell, getDomain, getEngine, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -16,13 +16,13 @@ export const post: RequestHandler = async (event) => {
|
||||
const found = await db.isDomainConfigured({ id, fqdn });
|
||||
if (found) {
|
||||
throw {
|
||||
message: `Domain ${getDomain(fqdn)} is already configured.`
|
||||
message: `Domain ${getDomain(fqdn).replace('www.', '')} is already configured.`
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let githubToken;
|
||||
export let application;
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import { page, session } from '$app/stores';
|
||||
import { get, post } from '$lib/api';
|
||||
import { getGithubToken } from '$lib/components/common';
|
||||
import { enhance, errorNotification } from '$lib/form';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const { id } = $page.params;
|
||||
@@ -30,19 +28,16 @@
|
||||
branch: undefined
|
||||
};
|
||||
let showSave = false;
|
||||
let token = null;
|
||||
|
||||
async function loadRepositoriesByPage(page = 0) {
|
||||
try {
|
||||
return await get(`${apiUrl}/installation/repositories?per_page=100&page=${page}`, {
|
||||
Authorization: `token ${token}`
|
||||
Authorization: `token ${$session.ghToken}`
|
||||
});
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function loadRepositories() {
|
||||
token = await getGithubToken({ apiUrl, githubToken, application });
|
||||
let page = 1;
|
||||
let reposCount = 0;
|
||||
const loadedRepos = await loadRepositoriesByPage();
|
||||
@@ -63,7 +58,7 @@
|
||||
selected.projectId = repositories.find((repo) => repo.full_name === selected.repository).id;
|
||||
try {
|
||||
branches = await get(`${apiUrl}/repos/${selected.repository}/branches`, {
|
||||
Authorization: `token ${token}`
|
||||
Authorization: `token ${$session.ghToken}`
|
||||
});
|
||||
return;
|
||||
} catch ({ error }) {
|
||||
@@ -74,13 +69,18 @@
|
||||
}
|
||||
async function isBranchAlreadyUsed() {
|
||||
try {
|
||||
return await get(
|
||||
const data = await get(
|
||||
`/applications/${id}/configuration/repository.json?repository=${selected.repository}&branch=${selected.branch}`
|
||||
);
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
if (data.used) {
|
||||
errorNotification('This branch is already used by another application.');
|
||||
showSave = false;
|
||||
return true;
|
||||
}
|
||||
showSave = true;
|
||||
} catch ({ error }) {
|
||||
showSave = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import cuid from 'cuid';
|
||||
import { goto } from '$app/navigation';
|
||||
import { del, get, post, put } from '$lib/api';
|
||||
|
||||
const { id } = $page.params;
|
||||
const from = $page.url.searchParams.get('from');
|
||||
|
||||
@@ -132,24 +131,20 @@
|
||||
}
|
||||
|
||||
async function isBranchAlreadyUsed() {
|
||||
const url = `/applications/${id}/configuration/repository.json?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`;
|
||||
|
||||
try {
|
||||
await get(url);
|
||||
const data = await get(
|
||||
`/applications/${id}/configuration/repository.json?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`
|
||||
);
|
||||
if (data.used) {
|
||||
errorNotification('This branch is already used by another application.');
|
||||
showSave = false;
|
||||
return true;
|
||||
}
|
||||
showSave = true;
|
||||
} catch (error) {
|
||||
showSave = false;
|
||||
return errorNotification('Branch already configured');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
// async function saveDeployKey(deployKeyId: number) {
|
||||
// try {
|
||||
// await post(updateDeployKeyIdUrl, { deployKeyId });
|
||||
// } catch (error) {
|
||||
// errorNotification(error);
|
||||
// throw new Error(error);
|
||||
// }
|
||||
// }
|
||||
async function checkSSHKey(sshkeyUrl) {
|
||||
try {
|
||||
return await post(sshkeyUrl, {});
|
||||
@@ -203,7 +198,6 @@
|
||||
const deployKeyFound = deployKeys.filter((dk) => dk.title === `${appId}-coolify-deploy-key`);
|
||||
if (deployKeyFound.length > 0) {
|
||||
for (const deployKey of deployKeyFound) {
|
||||
console.log(`${deployKeyUrl}/${deployKey.id}`);
|
||||
await del(
|
||||
`${deployKeyUrl}/${deployKey.id}`,
|
||||
{},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -21,7 +21,7 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,6 +36,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.configureBuildPack({ id, buildPack });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch, params, url, stuff }) => {
|
||||
const { application, ghToken } = stuff;
|
||||
const { application } = stuff;
|
||||
if (application?.buildPack && !url.searchParams.get('from')) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -14,8 +14,7 @@
|
||||
return {
|
||||
props: {
|
||||
...(await res.json()),
|
||||
application,
|
||||
ghToken
|
||||
application
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -43,7 +42,6 @@
|
||||
export let projectId;
|
||||
export let repository;
|
||||
export let branch;
|
||||
export let ghToken;
|
||||
export let type;
|
||||
export let application;
|
||||
|
||||
@@ -96,7 +94,7 @@
|
||||
}
|
||||
} else if (type === 'github') {
|
||||
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
||||
Authorization: `Bearer ${ghToken}`,
|
||||
Authorization: `Bearer ${$session.ghToken || ghToken}`,
|
||||
Accept: 'application/vnd.github.v2.json'
|
||||
});
|
||||
const packageJson = files.find(
|
||||
@@ -113,7 +111,7 @@
|
||||
foundConfig.buildPack = 'docker';
|
||||
} else if (packageJson) {
|
||||
const data = await get(`${packageJson.git_url}`, {
|
||||
Authorization: `Bearer ${ghToken}`,
|
||||
Authorization: `Bearer ${$session.ghToken}`,
|
||||
Accept: 'application/vnd.github.v2.raw'
|
||||
});
|
||||
const json = JSON.parse(data) || {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -12,6 +12,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.updateDeployKey({ id, deployKeyId });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -14,6 +14,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.configureDestinationForApplication({ id, destinationId });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -14,16 +14,14 @@ export const get: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const found = await db.isBranchAlreadyUsed({ repository, branch, id });
|
||||
if (found) {
|
||||
throw {
|
||||
error: `Branch ${branch} is already used by another application`
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 200
|
||||
status: 200,
|
||||
body: {
|
||||
used: found ? true : false
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,6 +40,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.configureGitRepository({ id, repository, branch, projectId, webhookToken });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ params, url, stuff }) => {
|
||||
const { application, githubToken, appId } = stuff;
|
||||
const { application, appId } = stuff;
|
||||
if (application?.branch && application?.repository && !url.searchParams.get('from')) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -10,7 +10,6 @@
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
githubToken,
|
||||
application,
|
||||
appId
|
||||
}
|
||||
@@ -20,7 +19,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
export let application;
|
||||
export let githubToken;
|
||||
export let appId;
|
||||
import GithubRepositories from './_GithubRepositories.svelte';
|
||||
import GitlabRepositories from './_GitlabRepositories.svelte';
|
||||
@@ -31,7 +29,7 @@
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center">
|
||||
{#if application.gitSource.type === 'github'}
|
||||
<GithubRepositories {application} {githubToken} />
|
||||
<GithubRepositories {application} />
|
||||
{:else if application.gitSource.type === 'gitlab'}
|
||||
<GitlabRepositories {application} {appId} />
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -13,6 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.configureGitsource({ id, gitSourceId });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -7,7 +7,7 @@ export const get: RequestHandler = async (event) => {
|
||||
try {
|
||||
return await db.getSshKey({ id });
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -15,6 +15,6 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
return await db.generateSshKey({ id });
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const del: RequestHandler = async (event) => {
|
||||
@@ -14,6 +14,6 @@ export const del: RequestHandler = async (event) => {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import cuid from 'cuid';
|
||||
import crypto from 'crypto';
|
||||
import { buildQueue } from '$lib/queues';
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
@@ -37,6 +37,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import { getGithubToken } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
@@ -14,37 +14,44 @@ export const get: RequestHandler = async (event) => {
|
||||
let githubToken = null;
|
||||
let ghToken = null;
|
||||
let isRunning = false;
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
const application = await db.getApplication({ id, teamId });
|
||||
const { gitSource } = application;
|
||||
if (gitSource?.type === 'github' && gitSource?.githubApp) {
|
||||
const payload = {
|
||||
iat: Math.round(new Date().getTime() / 1000),
|
||||
exp: Math.round(new Date().getTime() / 1000 + 60),
|
||||
iss: gitSource.githubApp.appId
|
||||
};
|
||||
githubToken = jsonwebtoken.sign(payload, gitSource.githubApp.privateKey, {
|
||||
algorithm: 'RS256'
|
||||
});
|
||||
ghToken = await getGithubToken({ apiUrl: gitSource.apiUrl, application, githubToken });
|
||||
if (!event.locals.session.data.ghToken) {
|
||||
const payload = {
|
||||
iat: Math.round(new Date().getTime() / 1000),
|
||||
exp: Math.round(new Date().getTime() / 1000 + 600),
|
||||
iss: gitSource.githubApp.appId
|
||||
};
|
||||
githubToken = jsonwebtoken.sign(payload, gitSource.githubApp.privateKey, {
|
||||
algorithm: 'RS256'
|
||||
});
|
||||
ghToken = await getGithubToken({ apiUrl: gitSource.apiUrl, application, githubToken });
|
||||
}
|
||||
}
|
||||
if (application.destinationDockerId) {
|
||||
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
||||
}
|
||||
return {
|
||||
const payload = {
|
||||
body: {
|
||||
isRunning,
|
||||
ghToken,
|
||||
githubToken,
|
||||
application,
|
||||
appId
|
||||
}
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
if (ghToken) {
|
||||
payload.headers = {
|
||||
'set-cookie': [`ghToken=${ghToken}; HttpOnly; Path=/; Max-Age=15778800;`]
|
||||
};
|
||||
}
|
||||
return payload;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,6 +89,6 @@ export const post: RequestHandler = async (event) => {
|
||||
});
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import type Prisma from '@prisma/client';
|
||||
import { getDomain, notNodeDeployments, staticDeployments } from '$lib/components/common';
|
||||
import { notNodeDeployments, staticDeployments } from '$lib/components/common';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { post } from '$lib/api';
|
||||
const { id } = $page.params;
|
||||
@@ -52,6 +52,7 @@
|
||||
let loading = false;
|
||||
let debug = application.settings.debug;
|
||||
let previews = application.settings.previews;
|
||||
let dualCerts = application.settings.dualCerts;
|
||||
|
||||
onMount(() => {
|
||||
domainEl.focus();
|
||||
@@ -64,8 +65,11 @@
|
||||
if (name === 'previews') {
|
||||
previews = !previews;
|
||||
}
|
||||
if (name === 'dualCerts') {
|
||||
dualCerts = !dualCerts;
|
||||
}
|
||||
try {
|
||||
await post(`/applications/${id}/settings.json`, { previews, debug });
|
||||
await post(`/applications/${id}/settings.json`, { previews, debug, dualCerts });
|
||||
return toast.push('Settings saved.');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@@ -252,7 +256,7 @@
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-cols-3">
|
||||
<label for="fqdn" class="pt-2">Domain (FQDN)</label>
|
||||
<label for="fqdn" class="relative pt-2">Domain (FQDN)</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
@@ -266,11 +270,21 @@
|
||||
required
|
||||
/>
|
||||
<Explainer
|
||||
text="If you specify <span class='text-green-600 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>To modify the domain, you must first stop the application."
|
||||
text="If you specify <span class='text-green-500 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-green-500 font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<Setting
|
||||
dataTooltip="Must be stopped to modify."
|
||||
disabled={isRunning}
|
||||
isCenter={false}
|
||||
bind:setting={dualCerts}
|
||||
title="Generate SSL for www and non-www?"
|
||||
description="It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-green-500'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both."
|
||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
{#if !staticDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="port">Port</label>
|
||||
@@ -285,6 +299,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="installCommand">Install Command</label>
|
||||
@@ -361,8 +376,7 @@
|
||||
<div class="flex space-x-1 pb-5 font-bold">
|
||||
<div class="title">Features</div>
|
||||
</div>
|
||||
<div class="px-4 pb-10 sm:px-6">
|
||||
<!-- <ul class="mt-2 divide-y divide-stone-800">
|
||||
<!-- <ul class="mt-2 divide-y divide-stone-800">
|
||||
<Setting
|
||||
bind:setting={forceSSL}
|
||||
on:click={() => changeSettings('forceSSL')}
|
||||
@@ -370,21 +384,24 @@
|
||||
description="Creates a https redirect for all requests from http and also generates a https certificate for the domain through Let's Encrypt."
|
||||
/>
|
||||
</ul> -->
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
<div class="px-10 pb-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
isCenter={false}
|
||||
bind:setting={previews}
|
||||
on:click={() => changeSettings('previews')}
|
||||
title="Enable MR/PR Previews"
|
||||
description="Creates previews from pull and merge requests."
|
||||
/>
|
||||
</ul>
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
isCenter={false}
|
||||
bind:setting={debug}
|
||||
on:click={() => changeSettings('debug')}
|
||||
title="Debug Logs"
|
||||
description="Enable debug logs during build phase. <br>(<span class='text-red-500'>sensitive information</span> could be visible in logs)"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,16 +15,32 @@
|
||||
let loading = true;
|
||||
let currentStatus;
|
||||
let streamInterval;
|
||||
let followingBuild;
|
||||
let followingInterval;
|
||||
let logsEl;
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
const cleanAnsiCodes = (str: string) => str.replace(/\x1B\[(\d+)m/g, '');
|
||||
|
||||
function followBuild() {
|
||||
followingBuild = !followingBuild;
|
||||
if (followingBuild) {
|
||||
followingInterval = setInterval(() => {
|
||||
logsEl.scrollTop = logsEl.scrollHeight;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}, 100);
|
||||
} else {
|
||||
window.clearInterval(followingInterval);
|
||||
}
|
||||
}
|
||||
async function streamLogs(sequence = 0) {
|
||||
try {
|
||||
let { logs: responseLogs, status } = await get(
|
||||
`/applications/${id}/logs/build/build.json?buildId=${buildId}&sequence=${sequence}`
|
||||
);
|
||||
currentStatus = status;
|
||||
logs = logs.concat(responseLogs);
|
||||
logs = logs.concat(responseLogs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
|
||||
loading = false;
|
||||
streamInterval = setInterval(async () => {
|
||||
if (status !== 'running') {
|
||||
@@ -38,18 +54,21 @@
|
||||
);
|
||||
status = data.status;
|
||||
currentStatus = status;
|
||||
logs = logs.concat(data.logs);
|
||||
|
||||
logs = logs.concat(data.logs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
|
||||
dispatch('updateBuildStatus', { status });
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}, 1000);
|
||||
} catch ({ error }) {
|
||||
console.log(error);
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
onDestroy(() => {
|
||||
clearInterval(streamInterval);
|
||||
clearInterval(followingInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
window.scrollTo(0, 0);
|
||||
@@ -60,12 +79,38 @@
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<div class="relative">
|
||||
<div class="relative ">
|
||||
{#if currentStatus === 'running'}
|
||||
<LoadingLogs />
|
||||
{/if}
|
||||
<div class="flex justify-end sticky top-0 p-2">
|
||||
<button
|
||||
on:click={followBuild}
|
||||
class="bg-transparent"
|
||||
data-tooltip="Follow logs"
|
||||
class:text-green-500={followingBuild}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-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" />
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="8" y1="12" x2="12" y2="16" />
|
||||
<line x1="12" y1="8" x2="12" y2="16" />
|
||||
<line x1="16" y1="12" x2="12" y2="16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words"
|
||||
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"
|
||||
bind:this={logsEl}
|
||||
>
|
||||
{#each logs as log}
|
||||
<div>{log.line + '\n'}</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -14,15 +14,15 @@ export const get: RequestHandler = async (event) => {
|
||||
where: { buildId, time: { gt: sequence } },
|
||||
orderBy: { time: 'asc' }
|
||||
});
|
||||
const { status } = await db.prisma.build.findFirst({ where: { id: buildId } });
|
||||
const data = await db.prisma.build.findFirst({ where: { id: buildId } });
|
||||
|
||||
return {
|
||||
body: {
|
||||
logs,
|
||||
status
|
||||
status: data?.status || 'running'
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dayjs } from '$lib/dayjs';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -35,6 +35,6 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
export let buildCount;
|
||||
|
||||
let buildId;
|
||||
$: buildId;
|
||||
|
||||
let skip = 0;
|
||||
let noMoreBuilds = buildCount < 5 || buildCount <= skip;
|
||||
@@ -83,7 +82,7 @@
|
||||
}
|
||||
async function loadBuild(build) {
|
||||
buildId = build;
|
||||
goto(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
await goto(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -92,45 +91,49 @@
|
||||
Build logs of <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-start space-x-2 px-10 pt-6 ">
|
||||
<div class="min-w-[16rem] space-y-2">
|
||||
{#each builds as build (build.id)}
|
||||
<div
|
||||
data-tooltip={new Intl.DateTimeFormat('default', dateOptions).format(
|
||||
new Date(build.createdAt)
|
||||
) + `\n${build.status}`}
|
||||
on:click={() => loadBuild(build.id)}
|
||||
class="tooltip-top flex cursor-pointer items-center justify-center rounded-r border-l-2 border-transparent py-4 no-underline transition-all duration-100 hover:bg-coolgray-400 hover:shadow-xl"
|
||||
class:bg-coolgray-400={buildId === build.id}
|
||||
class:border-red-500={build.status === 'failed'}
|
||||
class:border-green-500={build.status === 'success'}
|
||||
class:border-yellow-500={build.status === 'inprogress'}
|
||||
>
|
||||
<div class="flex-col px-2">
|
||||
<div class="text-sm font-bold">
|
||||
{application.branch}
|
||||
<div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
|
||||
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
|
||||
<div class="top-4 md:sticky">
|
||||
{#each builds as build, index (build.id)}
|
||||
<div
|
||||
data-tooltip={new Intl.DateTimeFormat('default', dateOptions).format(
|
||||
new Date(build.createdAt)
|
||||
) + `\n${build.status}`}
|
||||
on:click={() => loadBuild(build.id)}
|
||||
class:rounded-tr={index === 0}
|
||||
class:rounded-br={index === builds.length - 1}
|
||||
class="tooltip-top flex cursor-pointer items-center justify-center border-l-2 border-transparent py-4 no-underline transition-all duration-100 hover:bg-coolgray-400 hover:shadow-xl "
|
||||
class:bg-coolgray-400={buildId === build.id}
|
||||
class:border-red-500={build.status === 'failed'}
|
||||
class:border-green-500={build.status === 'success'}
|
||||
class:border-yellow-500={build.status === 'running'}
|
||||
>
|
||||
<div class="flex-col px-2">
|
||||
<div class="text-sm font-bold">
|
||||
{application.branch}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
{build.type}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
{build.type}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1" />
|
||||
<div class="flex-1" />
|
||||
|
||||
<div class="w-48 text-center text-xs">
|
||||
{#if build.status === 'running'}
|
||||
<div class="font-bold">Running</div>
|
||||
{:else}
|
||||
<div>{build.since}</div>
|
||||
<div>Finished in <span class="font-bold">{build.took}s</span></div>
|
||||
{/if}
|
||||
<div class="w-48 text-center text-xs">
|
||||
{#if build.status === 'running'}
|
||||
<div class="font-bold">Running</div>
|
||||
{:else}
|
||||
<div>{build.since}</div>
|
||||
<div>Finished in <span class="font-bold">{build.took}s</span></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{#if buildCount > 0 && !noMoreBuilds}
|
||||
<button class="w-full" on:click={loadMoreBuilds}>Load More</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}>Load More</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-96 flex-1">
|
||||
<div class="flex-1 md:w-96">
|
||||
{#if buildId}
|
||||
{#key buildId}
|
||||
<svelte:component this={BuildLog} {buildId} on:updateBuildStatus={updateBuildStatus} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dayjs } from '$lib/dayjs';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
@@ -48,6 +48,6 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,19 +27,23 @@
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { get } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
|
||||
let loadLogsInterval = null;
|
||||
let logs = [];
|
||||
let followingBuild;
|
||||
let followingInterval;
|
||||
let logsEl;
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
onMount(async () => {
|
||||
loadLogs();
|
||||
loadLogsInterval = setInterval(() => {
|
||||
loadLogs();
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
});
|
||||
onDestroy(() => {
|
||||
clearInterval(loadLogsInterval);
|
||||
clearInterval(followingInterval);
|
||||
});
|
||||
async function loadLogs() {
|
||||
try {
|
||||
@@ -50,6 +54,18 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
|
||||
function followBuild() {
|
||||
followingBuild = !followingBuild;
|
||||
if (followingBuild) {
|
||||
followingInterval = setInterval(() => {
|
||||
logsEl.scrollTop = logsEl.scrollHeight;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}, 100);
|
||||
} else {
|
||||
window.clearInterval(followingInterval);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
@@ -61,14 +77,42 @@
|
||||
{#if logs.length === 0}
|
||||
<div class="text-xl font-bold tracking-tighter">Waiting for the logs...</div>
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<div class="relative">
|
||||
<LoadingLogs />
|
||||
<div class="flex justify-end sticky top-0 p-2">
|
||||
<button
|
||||
on:click={followBuild}
|
||||
class="bg-transparent"
|
||||
data-tooltip="Follow logs"
|
||||
class:text-green-500={followingBuild}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-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" />
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="8" y1="12" x2="12" y2="16" />
|
||||
<line x1="12" y1="8" x2="12" y2="16" />
|
||||
<line x1="16" y1="12" x2="12" y2="16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 p-6 whitespace-pre-wrap break-words w-full"
|
||||
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12 overflow-y-scroll scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200"
|
||||
bind:this={logsEl}
|
||||
>
|
||||
{#each logs as log}
|
||||
{log + '\n'}
|
||||
{/each}
|
||||
<div class="px-2">
|
||||
{#each logs as log}
|
||||
{log + '\n'}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
@@ -39,6 +39,6 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,22 +6,43 @@
|
||||
import { page } from '$app/stores';
|
||||
import { del, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
if (name) value = 'ENCRYPTED';
|
||||
const dispatch = createEventDispatcher();
|
||||
let nameEl;
|
||||
let valueEl;
|
||||
const { id } = $page.params;
|
||||
|
||||
async function removeSecret() {
|
||||
try {
|
||||
await del(`/applications/${id}/secrets.json`, { name });
|
||||
return window.location.reload();
|
||||
dispatch('refresh');
|
||||
if (isNewSecret) {
|
||||
name = '';
|
||||
value = '';
|
||||
isBuildSecret = false;
|
||||
}
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function saveSecret() {
|
||||
const nameValid = nameEl.checkValidity();
|
||||
const valueValid = valueEl.checkValidity();
|
||||
if (!nameValid) {
|
||||
return nameEl.reportValidity();
|
||||
}
|
||||
if (!valueValid) {
|
||||
return valueEl.reportValidity();
|
||||
}
|
||||
|
||||
try {
|
||||
await post(`/applications/${id}/secrets.json`, { name, value, isBuildSecret });
|
||||
return window.location.reload();
|
||||
dispatch('refresh');
|
||||
if (isNewSecret) {
|
||||
name = '';
|
||||
value = '';
|
||||
isBuildSecret = false;
|
||||
}
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
@@ -33,101 +54,89 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-3xl pt-4">
|
||||
<div class="flex space-x-2">
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="secretName">Name</label>
|
||||
<input
|
||||
id="secretName"
|
||||
bind:value={name}
|
||||
placeholder="EXAMPLE_VARIABLE"
|
||||
class="w-64 border-2 border-transparent"
|
||||
readonly={!isNewSecret}
|
||||
class:hover:bg-coolgray-200={!isNewSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-flow-row">
|
||||
<label for="secretValue">Value (will be encrypted)</label>
|
||||
<input
|
||||
id="secretValue"
|
||||
bind:value
|
||||
placeholder="J$#@UIO%HO#$U%H"
|
||||
class="w-64 border-2 border-transparent"
|
||||
class:hover:bg-coolgray-200={!isNewSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
readonly={!isNewSecret}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-32 px-2 text-center">
|
||||
<div class="text-xs">Is build variable?</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<ul class="divide-y divide-stone-800">
|
||||
<li>
|
||||
<div
|
||||
type="button"
|
||||
on:click={setSecretValue}
|
||||
aria-pressed="false"
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
||||
class:bg-green-600={isBuildSecret}
|
||||
class:bg-stone-700={!isBuildSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
class:cursor-pointer={isNewSecret}
|
||||
>
|
||||
<span class="sr-only">Use isBuildSecret</span>
|
||||
<span
|
||||
class="pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow transition duration-200 ease-in-out"
|
||||
class:translate-x-5={isBuildSecret}
|
||||
class:translate-x-0={!isBuildSecret}
|
||||
>
|
||||
<span
|
||||
class=" absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-in"
|
||||
class:opacity-0={isBuildSecret}
|
||||
class:opacity-100={!isBuildSecret}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-3 w-3 bg-white text-red-600" fill="none" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-100 ease-out"
|
||||
aria-hidden="true"
|
||||
class:opacity-100={isBuildSecret}
|
||||
class:opacity-0={!isBuildSecret}
|
||||
>
|
||||
<svg
|
||||
class="h-3 w-3 bg-white text-green-600"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 12 12"
|
||||
>
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{#if isNewSecret}
|
||||
<div class="mt-6">
|
||||
<button class="w-20 bg-green-600 hover:bg-green-500" on:click={saveSecret}>Add</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mt-6">
|
||||
<button class="w-20 bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
||||
</div>
|
||||
{/if}
|
||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||
<input
|
||||
id="secretName"
|
||||
bind:this={nameEl}
|
||||
bind:value={name}
|
||||
required
|
||||
placeholder="EXAMPLE_VARIABLE"
|
||||
class="-mx-2 w-64 border-2 border-transparent"
|
||||
readonly={!isNewSecret}
|
||||
class:bg-transparent={!isNewSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
/>
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||
<input
|
||||
id="secretValue"
|
||||
bind:value
|
||||
bind:this={valueEl}
|
||||
required
|
||||
placeholder="J$#@UIO%HO#$U%H"
|
||||
class="-mx-2 w-64 border-2 border-transparent"
|
||||
class:bg-transparent={!isNewSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
readonly={!isNewSecret}
|
||||
/>
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-6 py-2 text-center text-sm font-medium text-white">
|
||||
<div
|
||||
type="button"
|
||||
on:click={setSecretValue}
|
||||
aria-pressed="false"
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
||||
class:bg-green-600={isBuildSecret}
|
||||
class:bg-stone-700={!isBuildSecret}
|
||||
class:opacity-50={!isNewSecret}
|
||||
class:cursor-not-allowed={!isNewSecret}
|
||||
class:cursor-pointer={isNewSecret}
|
||||
>
|
||||
<span class="sr-only">Use isBuildSecret</span>
|
||||
<span
|
||||
class="pointer-events-none relative inline-block h-5 w-5 transform rounded-full bg-white shadow transition duration-200 ease-in-out"
|
||||
class:translate-x-5={isBuildSecret}
|
||||
class:translate-x-0={!isBuildSecret}
|
||||
>
|
||||
<span
|
||||
class=" absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-200 ease-in"
|
||||
class:opacity-0={isBuildSecret}
|
||||
class:opacity-100={!isBuildSecret}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-3 w-3 bg-white text-red-600" fill="none" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="absolute inset-0 flex h-full w-full items-center justify-center transition-opacity duration-100 ease-out"
|
||||
aria-hidden="true"
|
||||
class:opacity-100={isBuildSecret}
|
||||
class:opacity-0={!isBuildSecret}
|
||||
>
|
||||
<svg class="h-3 w-3 bg-white text-green-600" fill="currentColor" viewBox="0 0 12 12">
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||
{#if isNewSecret}
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="w-24 bg-green-600 hover:bg-green-500" on:click={saveSecret}>Add</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex justify-center items-end">
|
||||
<button class="w-24 bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -18,7 +18,7 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ export const post: RequestHandler = async (event) => {
|
||||
const found = await db.isSecretExists({ id, name });
|
||||
if (found) {
|
||||
throw {
|
||||
error: `Secret ${name} already exists`
|
||||
error: `Secret ${name} already exists.`
|
||||
};
|
||||
} else {
|
||||
await db.createSecret({ id, name, value, isBuildSecret });
|
||||
@@ -42,7 +42,7 @@ export const post: RequestHandler = async (event) => {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
export const del: RequestHandler = async (event) => {
|
||||
@@ -58,6 +58,6 @@ export const del: RequestHandler = async (event) => {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,6 +24,15 @@
|
||||
export let application;
|
||||
import Secret from './_Secret.svelte';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { page } from '$app/stores';
|
||||
import { get } from '$lib/api';
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
async function refreshSecrets() {
|
||||
const data = await get(`/applications/${id}/secrets.json`);
|
||||
secrets = [...data.secrets];
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
@@ -31,11 +40,46 @@
|
||||
Secrets for <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<div class="flex-col justify-start space-y-1">
|
||||
{#each secrets as secret}
|
||||
<Secret name={secret.name} value={secret.value} isBuildSecret={secret.isBuildSecret} />
|
||||
{/each}
|
||||
<Secret isNewSecret />
|
||||
</div>
|
||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||
<table class="mx-auto">
|
||||
<thead class=" rounded-xl border-b border-coolgray-500">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white">Name</th
|
||||
>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||
>Value</th
|
||||
>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||
>Need during buildtime?</th
|
||||
>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-bold uppercase tracking-wider text-white"
|
||||
/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
{#each secrets as secret}
|
||||
{#key secret.id}
|
||||
<tr class="hover:bg-coolgray-200">
|
||||
<Secret
|
||||
name={secret.name}
|
||||
value={secret.value ? secret.value : 'ENCRYPTED'}
|
||||
isBuildSecret={secret.isBuildSecret}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
</tr>
|
||||
{/key}
|
||||
{/each}
|
||||
<tr>
|
||||
<Secret isNewSecret on:refresh={refreshSecrets} />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -8,12 +8,12 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
const { debug, previews } = await event.request.json();
|
||||
const { debug, previews, dualCerts } = await event.request.json();
|
||||
|
||||
try {
|
||||
await db.setApplicationSettings({ id, debug, previews });
|
||||
await db.setApplicationSettings({ id, debug, previews, dualCerts });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { removeProxyConfiguration } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
@@ -16,16 +16,15 @@ export const post: RequestHandler = async (event) => {
|
||||
id,
|
||||
teamId
|
||||
});
|
||||
const domain = getDomain(fqdn);
|
||||
if (destinationDockerId) {
|
||||
const docker = dockerInstance({ destinationDocker });
|
||||
await docker.engine.getContainer(id).stop();
|
||||
}
|
||||
await removeProxyConfiguration({ domain });
|
||||
await removeProxyConfiguration(fqdn);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -16,6 +16,6 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let applications: Array<Applications>;
|
||||
export let applications: Array<Application>;
|
||||
import { session } from '$app/stores';
|
||||
import Application from './_Application.svelte';
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -25,7 +25,7 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,72 +7,62 @@
|
||||
<div class="title">CouchDB</div>
|
||||
</div>
|
||||
<div class="px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
function generateUrl() {
|
||||
return browser
|
||||
? `${database.type}://${
|
||||
? `${database.type}://${database.type === 'redis' && ':'}${
|
||||
databaseDbUser ? databaseDbUser + ':' : ''
|
||||
}${databaseDbUserPassword}@${
|
||||
isPublic
|
||||
@@ -88,70 +88,60 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2 ">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="name"
|
||||
id="name"
|
||||
bind:value={database.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="name"
|
||||
id="name"
|
||||
bind:value={database.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="destination">Destination</label>
|
||||
<div class="col-span-2">
|
||||
{#if database.destinationDockerId}
|
||||
<div class="no-underline">
|
||||
<input
|
||||
value={database.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
readonly
|
||||
class="bg-transparent "
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if database.destinationDockerId}
|
||||
<div class="no-underline">
|
||||
<input
|
||||
value={database.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
readonly
|
||||
class="bg-transparent "
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="version">Version</label>
|
||||
<div class="col-span-2 ">
|
||||
<input value={database.version} readonly disabled class="bg-transparent " />
|
||||
</div>
|
||||
<input value={database.version} readonly disabled class="bg-transparent " />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="host">Host</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={false}
|
||||
readonly
|
||||
disabled
|
||||
id="host"
|
||||
name="host"
|
||||
value={database.id}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={false}
|
||||
readonly
|
||||
disabled
|
||||
id="host"
|
||||
name="host"
|
||||
value={database.id}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="publicPort">Port</label>
|
||||
<div class="col-span-2">
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
id="publicPort"
|
||||
readonly
|
||||
disabled
|
||||
name="publicPort"
|
||||
value={isPublic ? database.publicPort : privatePort}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
id="publicPort"
|
||||
readonly
|
||||
disabled
|
||||
name="publicPort"
|
||||
value={isPublic ? database.publicPort : privatePort}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2">
|
||||
@@ -166,44 +156,42 @@
|
||||
{:else if database.type === 'couchdb'}
|
||||
<CouchDb bind:database />
|
||||
{/if}
|
||||
<div class="grid grid-cols-3 items-center px-10 pb-8">
|
||||
<div class="grid grid-cols-2 items-center px-10 pb-8">
|
||||
<label for="url">Connection String</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
textarea={true}
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={false}
|
||||
id="url"
|
||||
name="url"
|
||||
readonly
|
||||
disabled
|
||||
value={databaseUrl}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
textarea={true}
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={false}
|
||||
id="url"
|
||||
name="url"
|
||||
readonly
|
||||
disabled
|
||||
value={databaseUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="flex space-x-1 pb-5 font-bold">
|
||||
<div class="title">Features</div>
|
||||
</div>
|
||||
<div class="px-4 pb-10 sm:px-6">
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
<div class="px-10 pb-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={isPublic}
|
||||
on:click={() => changeSettings('isPublic')}
|
||||
title="Set it public"
|
||||
description="Your database will be reachable over the internet. <br>Take security seriously in this case!"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
{#if database.type === 'redis'}
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={appendOnly}
|
||||
on:click={() => changeSettings('appendOnly')}
|
||||
title="Change append only mode"
|
||||
description="Useful if you would like to restore redis data from a backup.<br><span class='font-bold text-white'>Database restart is required.</span>"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,31 +7,27 @@
|
||||
<div class="title">MongoDB</div>
|
||||
</div>
|
||||
<div class="px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
readonly
|
||||
disabled
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
readonly
|
||||
disabled
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={true}
|
||||
readonly
|
||||
disabled
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={true}
|
||||
readonly
|
||||
disabled
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,72 +7,62 @@
|
||||
<div class="title">MySQL</div>
|
||||
</div>
|
||||
<div class=" px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,45 +7,39 @@
|
||||
<div class="title">PostgreSQL</div>
|
||||
</div>
|
||||
<div class="px-10">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
disabled={database.defaultDatabase}
|
||||
placeholder="eg: mydb"
|
||||
id="defaultDatabase"
|
||||
name="defaultDatabase"
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,32 +7,17 @@
|
||||
<div class="title">Redis</div>
|
||||
</div>
|
||||
<div class="px-10">
|
||||
<!-- <div class="grid grid-cols-3 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
placeholder="Generated automatically after start"
|
||||
id="dbUser"
|
||||
name="dbUser"
|
||||
bind:value={database.dbUser}
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<div class="col-span-2 ">
|
||||
<CopyPasswordField
|
||||
disabled
|
||||
readonly
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<CopyPasswordField
|
||||
disabled
|
||||
readonly
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="dbUserPassword"
|
||||
name="dbUserPassword"
|
||||
value={database.dbUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="grid grid-cols-3 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -14,6 +14,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.configureDestinationForDatabase({ id, destinationId });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -27,6 +27,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 201
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import { ErrorHandler, supportedDatabaseTypesAndVersions } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -31,6 +31,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 201
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler, stopDatabase } from '$lib/database';
|
||||
import { ErrorHandler, stopDatabase } from '$lib/database';
|
||||
import { deleteProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -17,6 +17,6 @@ export const del: RequestHandler = async (event) => {
|
||||
await db.removeDatabase({ id });
|
||||
return { status: 200 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { generateDatabaseConfiguration, getVersions, PrismaErrorHandler } from '$lib/database';
|
||||
import { generateDatabaseConfiguration, getVersions, ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -25,9 +25,7 @@ export const get: RequestHandler = async (event) => {
|
||||
state = 'running';
|
||||
}
|
||||
} catch (error) {
|
||||
// if (!error.stderr.includes('No such object')) {
|
||||
// console.log(error)
|
||||
// }
|
||||
//
|
||||
}
|
||||
}
|
||||
const configuration = generateDatabaseConfiguration(database);
|
||||
@@ -42,7 +40,7 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,6 +64,6 @@ export const post: RequestHandler = async (event) => {
|
||||
});
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { generateDatabaseConfiguration, PrismaErrorHandler } from '$lib/database';
|
||||
import { generateDatabaseConfiguration, ErrorHandler } from '$lib/database';
|
||||
import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -29,6 +29,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 201
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { asyncExecShell, createDirectories, getEngine, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { generateDatabaseConfiguration, PrismaErrorHandler } from '$lib/database';
|
||||
import { generateDatabaseConfiguration, ErrorHandler } from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
@@ -77,6 +77,6 @@ export const post: RequestHandler = async (event) => {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler, stopDatabase } from '$lib/database';
|
||||
import { ErrorHandler, stopDatabase } from '$lib/database';
|
||||
import { stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -20,6 +20,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
@@ -15,6 +15,6 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
let cannotDisable = settings.fqdn && destination.engine === '/var/run/docker.sock';
|
||||
// let scannedApps = [];
|
||||
let loading = false;
|
||||
let restarting = false;
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
try {
|
||||
@@ -42,6 +43,17 @@
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
} else if (state === true && destination.isCoolifyProxyUsed === false) {
|
||||
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||
try {
|
||||
await post(`/destinations/${id}/settings.json`, {
|
||||
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||
engine: destination.engine
|
||||
});
|
||||
await startProxy();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
async function changeProxySetting() {
|
||||
@@ -89,6 +101,25 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function forceRestartProxy() {
|
||||
const sure = confirm(
|
||||
'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.'
|
||||
);
|
||||
if (sure) {
|
||||
try {
|
||||
restarting = true;
|
||||
toast.push('Coolify Proxy restarting...');
|
||||
await post(`/destinations/${id}/restart.json`, {
|
||||
engine: destination.engine,
|
||||
fqdn: settings.fqdn
|
||||
});
|
||||
} catch ({ error }) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center px-6 pb-8">
|
||||
@@ -103,6 +134,12 @@
|
||||
disabled={loading}
|
||||
>{loading ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
<button
|
||||
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
||||
disabled={restarting}
|
||||
on:click|preventDefault={forceRestartProxy}
|
||||
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
||||
>
|
||||
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||
>Scan for applications</button
|
||||
> -->
|
||||
@@ -144,21 +181,18 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-start">
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
<Setting
|
||||
disabled={cannotDisable}
|
||||
bind:setting={destination.isCoolifyProxyUsed}
|
||||
on:click={changeProxySetting}
|
||||
isPadding={false}
|
||||
title="Use Coolify Proxy?"
|
||||
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||
cannotDisable
|
||||
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</ul>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
disabled={cannotDisable}
|
||||
bind:setting={destination.isCoolifyProxyUsed}
|
||||
on:click={changeProxySetting}
|
||||
title="Use Coolify Proxy?"
|
||||
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||
cannotDisable
|
||||
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { asyncExecShell, getEngine, getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -12,7 +12,8 @@ export const get: RequestHandler = async (event) => {
|
||||
try {
|
||||
const destination = await db.getDestination({ id, teamId });
|
||||
const settings = await db.listSettings();
|
||||
const state = await checkContainer(destination.engine, 'coolify-haproxy');
|
||||
const state =
|
||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
@@ -22,7 +23,7 @@ export const get: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -36,7 +37,7 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.updateDestination({ id, name, engine, network });
|
||||
return { status: 200 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,6 +51,6 @@ export const del: RequestHandler = async (event) => {
|
||||
await db.removeDestination({ id });
|
||||
return { status: 200 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
34
src/routes/destinations/[id]/restart.json.ts
Normal file
34
src/routes/destinations/[id]/restart.json.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import * as db from '$lib/database';
|
||||
import {
|
||||
configureCoolifyProxyOn,
|
||||
forceSSLOnApplication,
|
||||
setWwwRedirection,
|
||||
startCoolifyProxy,
|
||||
stopCoolifyProxy
|
||||
} 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 { engine, fqdn } = await event.request.json();
|
||||
|
||||
try {
|
||||
const domain = getDomain(fqdn);
|
||||
await stopCoolifyProxy(engine);
|
||||
await startCoolifyProxy(engine);
|
||||
await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
|
||||
await configureCoolifyProxyOn(fqdn);
|
||||
await setWwwRedirection(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) await forceSSLOnApplication(domain);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { asyncExecShell, getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -59,6 +59,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 404
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -13,6 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
await db.setDestinationSettings({ engine, isCoolifyProxyUsed });
|
||||
return { status: 200 };
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { startCoolifyProxy, stopCoolifyProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -16,6 +16,6 @@ export const post: RequestHandler = async (event) => {
|
||||
};
|
||||
} catch (error) {
|
||||
await stopCoolifyProxy(engine);
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { stopCoolifyProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
@@ -14,6 +14,6 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTeam, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { PrismaErrorHandler } from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (request) => {
|
||||
@@ -14,6 +14,6 @@ export const get: RequestHandler = async (request) => {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return PrismaErrorHandler(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user