mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac19ea5407 | ||
|
|
d19b05b970 | ||
|
|
a0795136ac | ||
|
|
d2566e345a | ||
|
|
66cd7cf90e | ||
|
|
9a599981ef | ||
|
|
f51f7bc82a | ||
|
|
dbcbac0137 | ||
|
|
e722f8a87c | ||
|
|
61679749eb | ||
|
|
23e12c9c44 | ||
|
|
6da78cd3e5 | ||
|
|
78ce8100a3 | ||
|
|
76ba338b45 | ||
|
|
823fe2deb2 | ||
|
|
cb90f692f2 | ||
|
|
0325343ede | ||
|
|
69d1556a1d | ||
|
|
2daa043840 | ||
|
|
f340ca9d05 | ||
|
|
02abd038fa | ||
|
|
b9da68ec28 | ||
|
|
88b3910d80 | ||
|
|
160412f6e4 | ||
|
|
59a86b25fc | ||
|
|
49e58b39f5 | ||
|
|
58e0757bbd | ||
|
|
5ff4197572 | ||
|
|
b56e28d27a | ||
|
|
c3d39e1dd4 | ||
|
|
716aa36bfd | ||
|
|
f01460170e | ||
|
|
a414ce282d | ||
|
|
6c32f3b130 | ||
|
|
4cf907c572 | ||
|
|
b28baaa5aa | ||
|
|
980dea64e0 | ||
|
|
c340f6436f | ||
|
|
54376fd105 | ||
|
|
ef006578b2 | ||
|
|
b0b1ee0c60 | ||
|
|
4e2026aa2d | ||
|
|
e0e50b4bd5 | ||
|
|
c9b52f1310 | ||
|
|
0195213dfb | ||
|
|
d6225cbde3 | ||
|
|
7b4c194b97 | ||
|
|
a5ecff24a3 | ||
|
|
c9c003dc9b | ||
|
|
fd95936219 | ||
|
|
15a3fd4456 | ||
|
|
df896542e4 | ||
|
|
8927e81274 | ||
|
|
340f061827 | ||
|
|
15cbac97c2 | ||
|
|
bb32d0f7d1 | ||
|
|
c370fba9ba | ||
|
|
6e32421172 | ||
|
|
6643687c0a | ||
|
|
ed01e78d77 | ||
|
|
93aed52f88 | ||
|
|
bb6d1fd6a3 | ||
|
|
6e33179fc2 | ||
|
|
277fd167cf | ||
|
|
98e8d5170b | ||
|
|
11ee1651ae | ||
|
|
0dfcf9b1e6 | ||
|
|
08f57ac5bc | ||
|
|
7095e781e9 | ||
|
|
df18b93809 | ||
|
|
0c2e028b38 | ||
|
|
80cb1bc129 | ||
|
|
74c1cb51f6 | ||
|
|
2e864bddf9 | ||
|
|
e60ae91b5d | ||
|
|
d606cd86a0 | ||
|
|
bc463c37f4 | ||
|
|
76c1480903 | ||
|
|
6f312caf8b | ||
|
|
980d8d374f | ||
|
|
add441675d | ||
|
|
d3d9754277 | ||
|
|
aa5e2edbc5 |
@@ -2,6 +2,12 @@
|
||||
|
||||
An open-source & self-hostable Heroku / Netlify alternative.
|
||||
|
||||
## Demo instance
|
||||
|
||||
https://demo.coolify.io/
|
||||
|
||||
(If it is unresponsible, that means someone overloaded the server. 🙃)
|
||||
|
||||
## Installation
|
||||
|
||||
Installation is automated with the following command:
|
||||
|
||||
33
package.json
33
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.0.17",
|
||||
"version": "2.0.24",
|
||||
"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",
|
||||
@@ -25,31 +25,31 @@
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-node": "1.0.0-next.68",
|
||||
"@sveltejs/adapter-node": "1.0.0-next.69",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.28",
|
||||
"@sveltejs/kit": "1.0.0-next.278",
|
||||
"@sveltejs/kit": "1.0.0-next.283",
|
||||
"@types/bcrypt": "5.0.0",
|
||||
"@types/js-cookie": "3.0.1",
|
||||
"@types/node": "17.0.18",
|
||||
"@types/node": "17.0.20",
|
||||
"@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",
|
||||
"@zerodevx/svelte-toast": "0.7.0",
|
||||
"autoprefixer": "10.4.2",
|
||||
"cross-var": "1.1.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-svelte3": "3.2.1",
|
||||
"eslint-config-prettier": "8.4.0",
|
||||
"eslint-plugin-svelte3": "3.4.0",
|
||||
"husky": "7.0.4",
|
||||
"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.2",
|
||||
"prisma": "3.10.0",
|
||||
"svelte": "3.46.4",
|
||||
"svelte-check": "2.4.3",
|
||||
"svelte-preprocess": "4.10.3",
|
||||
"svelte-check": "2.4.5",
|
||||
"svelte-preprocess": "4.10.4",
|
||||
"tailwindcss": "3.0.23",
|
||||
"ts-node": "10.5.0",
|
||||
"tslib": "2.3.1",
|
||||
@@ -58,10 +58,10 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@prisma/client": "3.9.2",
|
||||
"@prisma/client": "3.10.0",
|
||||
"@sentry/node": "6.17.9",
|
||||
"bcrypt": "5.0.1",
|
||||
"bullmq": "1.73.0",
|
||||
"bullmq": "1.74.2",
|
||||
"compare-versions": "4.1.3",
|
||||
"cookie": "0.4.2",
|
||||
"cuid": "2.1.8",
|
||||
@@ -69,11 +69,12 @@
|
||||
"dockerode": "3.3.1",
|
||||
"dotenv-extended": "2.9.0",
|
||||
"generate-password": "1.7.0",
|
||||
"get-port": "6.1.0",
|
||||
"get-port": "6.1.1",
|
||||
"got": "12.0.1",
|
||||
"js-cookie": "3.0.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"mustache": "^4.2.0",
|
||||
"node-forge": "1.2.1",
|
||||
"svelte-kit-cookie-session": "2.1.2",
|
||||
"tailwindcss-scrollbar": "^0.1.0",
|
||||
|
||||
142
pnpm-lock.yaml
generated
142
pnpm-lock.yaml
generated
@@ -2,21 +2,21 @@ lockfileVersion: 5.3
|
||||
|
||||
specifiers:
|
||||
'@iarna/toml': 2.2.5
|
||||
'@prisma/client': 3.9.2
|
||||
'@prisma/client': 3.10.0
|
||||
'@sentry/node': 6.17.9
|
||||
'@sveltejs/adapter-node': 1.0.0-next.68
|
||||
'@sveltejs/adapter-node': 1.0.0-next.69
|
||||
'@sveltejs/adapter-static': 1.0.0-next.28
|
||||
'@sveltejs/kit': 1.0.0-next.278
|
||||
'@sveltejs/kit': 1.0.0-next.283
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
'@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
|
||||
'@zerodevx/svelte-toast': 0.7.0
|
||||
autoprefixer: 10.4.2
|
||||
bcrypt: 5.0.1
|
||||
bullmq: 1.73.0
|
||||
bullmq: 1.74.2
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cross-var: 1.1.0
|
||||
@@ -25,26 +25,27 @@ specifiers:
|
||||
dockerode: 3.3.1
|
||||
dotenv-extended: 2.9.0
|
||||
eslint: 7.32.0
|
||||
eslint-config-prettier: 8.3.0
|
||||
eslint-plugin-svelte3: 3.2.1
|
||||
eslint-config-prettier: 8.4.0
|
||||
eslint-plugin-svelte3: 3.4.0
|
||||
generate-password: 1.7.0
|
||||
get-port: 6.1.0
|
||||
get-port: 6.1.1
|
||||
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.4
|
||||
mustache: ^4.2.0
|
||||
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.2
|
||||
prisma: 3.10.0
|
||||
svelte: 3.46.4
|
||||
svelte-check: 2.4.3
|
||||
svelte-check: 2.4.5
|
||||
svelte-kit-cookie-session: 2.1.2
|
||||
svelte-preprocess: 4.10.3
|
||||
svelte-preprocess: 4.10.4
|
||||
tailwindcss: 3.0.23
|
||||
tailwindcss-scrollbar: ^0.1.0
|
||||
ts-node: 10.5.0
|
||||
@@ -54,10 +55,10 @@ specifiers:
|
||||
|
||||
dependencies:
|
||||
'@iarna/toml': 2.2.5
|
||||
'@prisma/client': 3.9.2_prisma@3.9.2
|
||||
'@prisma/client': 3.10.0_prisma@3.10.0
|
||||
'@sentry/node': 6.17.9
|
||||
bcrypt: 5.0.1
|
||||
bullmq: 1.73.0
|
||||
bullmq: 1.74.2
|
||||
compare-versions: 4.1.3
|
||||
cookie: 0.4.2
|
||||
cuid: 2.1.8
|
||||
@@ -65,44 +66,45 @@ dependencies:
|
||||
dockerode: 3.3.1
|
||||
dotenv-extended: 2.9.0
|
||||
generate-password: 1.7.0
|
||||
get-port: 6.1.0
|
||||
get-port: 6.1.1
|
||||
got: 12.0.1
|
||||
js-cookie: 3.0.1
|
||||
js-yaml: 4.1.0
|
||||
jsonwebtoken: 8.5.1
|
||||
mustache: 4.2.0
|
||||
node-forge: 1.2.1
|
||||
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.68
|
||||
'@sveltejs/adapter-node': 1.0.0-next.69
|
||||
'@sveltejs/adapter-static': 1.0.0-next.28
|
||||
'@sveltejs/kit': 1.0.0-next.278_svelte@3.46.4
|
||||
'@sveltejs/kit': 1.0.0-next.283_svelte@3.46.4
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/js-cookie': 3.0.1
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
'@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
|
||||
'@zerodevx/svelte-toast': 0.6.3
|
||||
'@zerodevx/svelte-toast': 0.7.0
|
||||
autoprefixer: 10.4.2_postcss@8.4.6
|
||||
cross-var: 1.1.0
|
||||
eslint: 7.32.0
|
||||
eslint-config-prettier: 8.3.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.46.4
|
||||
eslint-config-prettier: 8.4.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.4.0_eslint@7.32.0+svelte@3.46.4
|
||||
husky: 7.0.4
|
||||
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.2
|
||||
prisma: 3.10.0
|
||||
svelte: 3.46.4
|
||||
svelte-check: 2.4.3_postcss@8.4.6+svelte@3.46.4
|
||||
svelte-preprocess: 4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9
|
||||
svelte-check: 2.4.5_postcss@8.4.6+svelte@3.46.4
|
||||
svelte-preprocess: 4.10.4_88b359da5cac6d8f6ee1bbb7080a3fa9
|
||||
tailwindcss: 3.0.23_c940fbabf228b85b1c73d314b43e31f1
|
||||
ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2
|
||||
ts-node: 10.5.0_e04e69b201f218c8d0d59acefc9ea8a6
|
||||
tslib: 2.3.1
|
||||
typescript: 4.5.5
|
||||
|
||||
@@ -252,10 +254,10 @@ packages:
|
||||
fastq: 1.13.0
|
||||
dev: true
|
||||
|
||||
/@prisma/client/3.9.2_prisma@3.9.2:
|
||||
/@prisma/client/3.10.0_prisma@3.10.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-VlEIYVMyfFZHbVBOlunPl47gmP/Z0zzPjPj8I7uKEIaABqrUy50ru3XS0aZd8GFvevVwt7p91xxkUjNjrWhKAQ==
|
||||
integrity: sha512-6P4sV7WFuODSfSoSEzCH1qfmWMrCUBk1LIIuTbQf6m1LI/IOpLN4lnqGDmgiBGprEzuWobnGLfe9YsXLn0inrg==
|
||||
}
|
||||
engines: { node: '>=12.6' }
|
||||
requiresBuild: true
|
||||
@@ -265,21 +267,21 @@ packages:
|
||||
prisma:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@prisma/engines-version': 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009
|
||||
prisma: 3.9.2
|
||||
'@prisma/engines-version': 3.10.0-50.73e60b76d394f8d37d8ebd1f8918c79029f0db86
|
||||
prisma: 3.10.0
|
||||
dev: false
|
||||
|
||||
/@prisma/engines-version/3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009:
|
||||
/@prisma/engines-version/3.10.0-50.73e60b76d394f8d37d8ebd1f8918c79029f0db86:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-5Dh+qTDhpPR66w6NNAnPs+/W/Qt4r1DSd+qhfPFcDThUK4uxoZKGlPb2IYQn5LL+18aIGnmteDf7BnVMmvBNSQ==
|
||||
integrity: sha512-cVYs5gyQH/qyut24hUvDznCfPrWiNMKNfPb9WmEoiU6ihlkscIbCfkmuKTtspVLWRdl0LqjYEC7vfnPv17HWhw==
|
||||
}
|
||||
dev: false
|
||||
|
||||
/@prisma/engines/3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009:
|
||||
/@prisma/engines/3.10.0-50.73e60b76d394f8d37d8ebd1f8918c79029f0db86:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==
|
||||
integrity: sha512-LjRssaWu9w2SrXitofnutRIyURI7l0veQYIALz7uY4shygM9nMcK3omXcObRm7TAcw3Z+9ytfK1B+ySOsOesxQ==
|
||||
}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
@@ -394,10 +396,10 @@ packages:
|
||||
engines: { node: '>=10' }
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-node/1.0.0-next.68:
|
||||
/@sveltejs/adapter-node/1.0.0-next.69:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-MiEjtl15Aupm6bjirVlq0kkc9AL8qDXz/blsh4jYMsaiidmcEHeDgfZQFM5YiXy95DbxV30MAkhwCQiYK/J8Kw==
|
||||
integrity: sha512-tVKwJ8vYG4NGFJ5L+tRuyRglGPaJ1khNqTKq4bYIUahk/pjXIu9USbMmFtNHd6IyDdxjPtRoVwHubAYfTQLTpg==
|
||||
}
|
||||
dependencies:
|
||||
tiny-glob: 0.2.9
|
||||
@@ -412,10 +414,10 @@ packages:
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.278_svelte@3.46.4:
|
||||
/@sveltejs/kit/1.0.0-next.283_svelte@3.46.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WT93Wnu05X9WG9BMMk/dj0gy6R7iXm9aXRDVgmIl9z8jT2ukejgmkhi5IwBYrK0OMIUALRVfukn+iy+srPc91Q==
|
||||
integrity: sha512-QFhL7cdBKXUKhJ3sHZnL7q07Eohc54N7OpX2ZsJuNTA43Z6vX/HF/Jh4TK62kqT+thDVlT5J1iEEMppxA2IxoQ==
|
||||
}
|
||||
engines: { node: '>=14.13' }
|
||||
hasBin: true
|
||||
@@ -504,7 +506,7 @@ packages:
|
||||
integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
dev: true
|
||||
|
||||
/@types/cacheable-request/6.0.2:
|
||||
@@ -515,7 +517,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.1
|
||||
'@types/keyv': 3.1.3
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
'@types/responselike': 1.0.0
|
||||
dev: false
|
||||
|
||||
@@ -546,7 +548,7 @@ packages:
|
||||
integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
dev: false
|
||||
|
||||
/@types/node-forge/1.0.0:
|
||||
@@ -555,13 +557,13 @@ packages:
|
||||
integrity: sha512-h0bgwPKq5u99T9Gor4qtV1lCZ41xNkai0pie1n/a2mh2/4+jENWOlo7AJ4YKxTZAnSZ8FRurUpdIN7ohaPPuHA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
dev: true
|
||||
|
||||
/@types/node/17.0.18:
|
||||
/@types/node/17.0.20:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==
|
||||
integrity: sha512-Q15Clj3lZSLnhVA6yKw1G7SQz46DeL9gO1TEgfK1OQGvMdQ6TUWmCeWf1QBUNkw2BDfV52i2YuYd9OF3ZwGhjw==
|
||||
}
|
||||
|
||||
/@types/parse-json/4.0.0:
|
||||
@@ -584,7 +586,7 @@ packages:
|
||||
integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
dev: false
|
||||
|
||||
/@types/sass/1.16.1:
|
||||
@@ -593,7 +595,7 @@ packages:
|
||||
integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ==
|
||||
}
|
||||
dependencies:
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.31.1_5d7752337e5ea49772097d8af1823bf9:
|
||||
@@ -722,10 +724,10 @@ packages:
|
||||
eslint-visitor-keys: 2.1.0
|
||||
dev: true
|
||||
|
||||
/@zerodevx/svelte-toast/0.6.3:
|
||||
/@zerodevx/svelte-toast/0.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-k0W1JFoqHIcIQaP9ij99+Rv0ugaQSSNwOuNwwmTGRjWtIqrQr+ExLDE8LQGXLlJIprqDyMWB4lJkUql/r0RAtA==
|
||||
integrity: sha512-Xvpy1dTE/bUcYJKrbBxc5KuXtbwhyUjEzmTNtg92bS558SvsH6FDu02pgFbBWNjK9g9f2eQCDBhBhJM4q6vhDw==
|
||||
}
|
||||
dev: true
|
||||
|
||||
@@ -1748,10 +1750,10 @@ packages:
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/bullmq/1.73.0:
|
||||
/bullmq/1.74.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+BF7yeGagYD/iMkM3FA8Wvb3j3MyKE/OdXv404+nQjUsKXfL7PbqX5NSA9lBtFzOdyFx9ZWyKRnBwuGQsLfM0w==
|
||||
integrity: sha512-qf0xjF3NDbZqi6ovxAA9QPrPdEgl3BrdYzQO5Pr+ECNIfscLpSXz87kVXI0oTPeDqxw5EJx4sh8EFf2RvVPYTg==
|
||||
}
|
||||
dependencies:
|
||||
cron-parser: 2.18.0
|
||||
@@ -2706,10 +2708,10 @@ packages:
|
||||
engines: { node: '>=10' }
|
||||
dev: true
|
||||
|
||||
/eslint-config-prettier/8.3.0_eslint@7.32.0:
|
||||
/eslint-config-prettier/8.4.0_eslint@7.32.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==
|
||||
integrity: sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw==
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -2718,10 +2720,10 @@ packages:
|
||||
eslint: 7.32.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-svelte3/3.2.1_eslint@7.32.0+svelte@3.46.4:
|
||||
/eslint-plugin-svelte3/3.4.0_eslint@7.32.0+svelte@3.46.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q==
|
||||
integrity: sha512-MIQUTuRv3o7LyQ+360qOc9mLT35j1I5YzHr04g/UDcvJTpg0X/kHWELY99ve869Rp/9wjqD7I26Aq5H8OH5RIg==
|
||||
}
|
||||
engines: { node: '>=10' }
|
||||
peerDependencies:
|
||||
@@ -3116,10 +3118,10 @@ packages:
|
||||
engines: { node: '>=8' }
|
||||
dev: false
|
||||
|
||||
/get-port/6.1.0:
|
||||
/get-port/6.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JKnPFW/G2ZRirH/25sLK1aLBQktJfQLixzMMuMBP8A2G/ivSaIwdTnlJeO7PWeyhyIGVorezNf6+CXZU9i0cIQ==
|
||||
integrity: sha512-RQOsDPSd2PcoLwakY1dwEtLiAbTR7IfmnxsKswfcHEfRKKbhWAG2R5Qo7C8ga6Ne4Mq4lFbogXfDGNfqFxwAaw==
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
dev: false
|
||||
@@ -4091,6 +4093,14 @@ packages:
|
||||
msgpackr-extract: 1.0.15
|
||||
dev: false
|
||||
|
||||
/mustache/4.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
|
||||
}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/nan/2.15.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4399,7 +4409,7 @@ packages:
|
||||
dependencies:
|
||||
import-cwd: 3.0.0
|
||||
lilconfig: 2.0.4
|
||||
ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2
|
||||
ts-node: 10.5.0_e04e69b201f218c8d0d59acefc9ea8a6
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
@@ -4488,16 +4498,16 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/prisma/3.9.2:
|
||||
/prisma/3.10.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-i9eK6cexV74OgeWaH3+e6S07kvC9jEZTl6BqtBH398nlCU0tck7mE9dicY6YQd+euvMjjCtY89q4NgmaPnUsSg==
|
||||
integrity: sha512-dAld12vtwdz9Rz01nOjmnXe+vHana5PSog8t0XGgLemKsUVsaupYpr74AHaS3s78SaTS5s2HOghnJF+jn91ZrA==
|
||||
}
|
||||
engines: { node: '>=12.6' }
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@prisma/engines': 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009
|
||||
'@prisma/engines': 3.10.0-50.73e60b76d394f8d37d8ebd1f8918c79029f0db86
|
||||
dev: true
|
||||
|
||||
/private/0.1.8:
|
||||
@@ -5162,10 +5172,10 @@ packages:
|
||||
engines: { node: '>= 0.4' }
|
||||
dev: true
|
||||
|
||||
/svelte-check/2.4.3_postcss@8.4.6+svelte@3.46.4:
|
||||
/svelte-check/2.4.5_postcss@8.4.6+svelte@3.46.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-0zJMMgqYHoP7QEG3tfc5DekpHAOqoy4QOL8scWMSdHIpVVDVC0MuYK57nFyj3XVTW8Zfm85FlgnAdQYsVmST2Q==
|
||||
integrity: sha512-nRft8BbG2wcxyCdHDZ7X43xLcvDzua3xLwq6wzHGcAF3ka3Jyhv2rvgq0+SF9NwHLMefp9C2XkM6etzsxK/cMQ==
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -5179,7 +5189,7 @@ packages:
|
||||
sade: 1.7.4
|
||||
source-map: 0.7.3
|
||||
svelte: 3.46.4
|
||||
svelte-preprocess: 4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9
|
||||
svelte-preprocess: 4.10.4_88b359da5cac6d8f6ee1bbb7080a3fa9
|
||||
typescript: 4.5.5
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@@ -5212,10 +5222,10 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess/4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9:
|
||||
/svelte-preprocess/4.10.4_88b359da5cac6d8f6ee1bbb7080a3fa9:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ttw17lJfb/dx2ZJT9sesaXT5l7mPQ9Apx1H496Kli3Hkk7orIRGpOw6rCPkRNzr6ueVPqb4vzodS5x7sBFhKHw==
|
||||
integrity: sha512-fuwol0N4UoHsNQolLFbMqWivqcJ9N0vfWO9IuPAiX/5okfoGXURyJ6nECbuEIv0nU3M8Xe2I1ONNje2buk7l6A==
|
||||
}
|
||||
engines: { node: '>= 9.11.2' }
|
||||
requiresBuild: true
|
||||
@@ -5420,7 +5430,7 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/ts-node/10.5.0_f3bd4037939c2ed2942ba074291f8ef2:
|
||||
/ts-node/10.5.0_e04e69b201f218c8d0d59acefc9ea8a6:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==
|
||||
@@ -5442,7 +5452,7 @@ packages:
|
||||
'@tsconfig/node12': 1.0.9
|
||||
'@tsconfig/node14': 1.0.1
|
||||
'@tsconfig/node16': 1.0.2
|
||||
'@types/node': 17.0.18
|
||||
'@types/node': 17.0.20
|
||||
acorn: 8.5.0
|
||||
acorn-walk: 8.2.0
|
||||
arg: 4.1.3
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- RedefineTables
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_Setting" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"fqdn" TEXT,
|
||||
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||
"proxyPassword" TEXT NOT NULL,
|
||||
"proxyUser" TEXT NOT NULL,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "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");
|
||||
PRAGMA foreign_key_check;
|
||||
PRAGMA foreign_keys=ON;
|
||||
2
prisma/migrations/20220301101928_proxyhash/migration.sql
Normal file
2
prisma/migrations/20220301101928_proxyhash/migration.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Setting" ADD COLUMN "proxyHash" TEXT;
|
||||
@@ -12,8 +12,11 @@ model Setting {
|
||||
fqdn String? @unique
|
||||
isRegistrationEnabled Boolean @default(false)
|
||||
dualCerts Boolean @default(false)
|
||||
minPort Int @default(9000)
|
||||
maxPort Int @default(9100)
|
||||
proxyPassword String
|
||||
proxyUser String
|
||||
proxyHash String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
@@ -52,9 +55,9 @@ model Team {
|
||||
permissions Permission[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
database Database[] @relation(fields: [databaseId], references: [id])
|
||||
database Database[] @relation(references: [id])
|
||||
databaseId String?
|
||||
service Service[] @relation(fields: [serviceId], references: [id])
|
||||
service Service[] @relation(references: [id])
|
||||
serviceId String?
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,15 @@ async function main() {
|
||||
proxyUser: cuid()
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await prisma.setting.update({
|
||||
where: {
|
||||
id: settingsFound.id
|
||||
},
|
||||
data: {
|
||||
proxyHash: null
|
||||
}
|
||||
});
|
||||
}
|
||||
const localDocker = await prisma.destinationDocker.findFirst({
|
||||
where: { engine: '/var/run/docker.sock' }
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
clear
|
||||
ARG1=$1
|
||||
WHO=$(whoami)
|
||||
APP_ID=$(cat /proc/sys/kernel/random/uuid)
|
||||
RANDOM_SECRET=$(echo $(($(date +%s%N) / 1000000)) | sha256sum | base64 | head -c 32)
|
||||
SENTRY_DSN="https://9e7a74326f29422584d2d0bebdc8b7d3@o1082494.ingest.sentry.io/6091062"
|
||||
DOCKER_MAJOR=20
|
||||
DOCKER_MINOR=10
|
||||
DOCKER_VERSION_OK="nok"
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
if [ $ARG1 ] && [ $ARG1 == "-d" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
function errorchecker() {
|
||||
exitCode=$?
|
||||
if [ $exitCode -ne "0" ]; then
|
||||
echo "$0 exited unexpectedly with status: $exitCode"
|
||||
exit $exitCode
|
||||
fi
|
||||
}
|
||||
trap 'errorchecker' EXIT
|
||||
|
||||
echo -e "Welcome to Coolify installer! \n"
|
||||
echo "This script will install all the required packages and services to run Coolify."
|
||||
echo -e "If you want to install Coolify on a different OS, please open an issue on Github to get supported version.\n\n"
|
||||
|
||||
echo -e "To see what I'm doing, please check:"
|
||||
echo -e "https://github.com/coollabsio/get.coollabs.io/blob/main/static/coolify/install_v2.sh\n\n"
|
||||
|
||||
if [ $WHO != 'root' ]; then
|
||||
echo 'Run as root please: sudo sh -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$(command -v docker)" ]; then
|
||||
while true; do
|
||||
read -p "Docker Engine not found, should I install it automatically? [Yy/Nn] " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
sh -c "$(curl -fsSL https://get.docker.com)"
|
||||
break
|
||||
;;
|
||||
[Nn]*)
|
||||
echo "Please install docker manually and update it to the latest, but at least to $DOCKER_MAJOR.$DOCKER_MINOR"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Please answer Y or N." ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
SERVER_VERSION=$(docker version -f "{{.Server.Version}}")
|
||||
SERVER_VERSION_MAJOR=$(echo "$SERVER_VERSION" | cut -d'.' -f 1)
|
||||
SERVER_VERSION_MINOR=$(echo "$SERVER_VERSION" | cut -d'.' -f 2)
|
||||
|
||||
if [ "$SERVER_VERSION_MAJOR" -ge "$DOCKER_MAJOR" ] &&
|
||||
[ "$SERVER_VERSION_MINOR" -ge "$DOCKER_MINOR" ]; then
|
||||
DOCKER_VERSION_OK="ok"
|
||||
fi
|
||||
|
||||
if [ $DOCKER_VERSION_OK == 'nok' ]; then
|
||||
echo "Docker version less than $DOCKER_MAJOR.$DOCKER_MINOR, please update it to at least to $DOCKER_MAJOR.$DOCKER_MINOR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Adding docker daemon configuration
|
||||
cat <<EOF >/etc/docker/daemon.json
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "100m",
|
||||
"max-file": "5"
|
||||
},
|
||||
"features": {
|
||||
"buildkit": true
|
||||
},
|
||||
"live-restore": true
|
||||
}
|
||||
EOF
|
||||
|
||||
# Restarting docker daemon
|
||||
sh -c "systemctl daemon-reload && systemctl restart docker"
|
||||
|
||||
# Downloading docker compose cli plugin
|
||||
mkdir -p ~/.docker/cli-plugins/
|
||||
curl -SL https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
|
||||
chmod +x ~/.docker/cli-plugins/docker-compose
|
||||
|
||||
# Making base directory for coolify
|
||||
if [ ! -d coolify ]; then
|
||||
mkdir coolify
|
||||
fi
|
||||
|
||||
if [ -f coolify/.env ]; then
|
||||
echo -e "Coolify is already installed, using some of the existing settings."
|
||||
else
|
||||
echo "COOLIFY_APP_ID=$APP_ID
|
||||
COOLIFY_SECRET_KEY=$RANDOM_SECRET
|
||||
COOLIFY_DATABASE_URL=file:../db/prod.db
|
||||
COOLIFY_SENTRY_DSN=$SENTRY_DSN
|
||||
COOLIFY_HOSTED_ON=docker" > coolify/.env
|
||||
fi
|
||||
|
||||
cd coolify && docker run -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db-sqlite coollabsio/coolify:latest /bin/sh -c "env | grep COOLIFY > .env && docker compose up -d --force-recreate"
|
||||
|
||||
echo -e "Congratulations! Your coolify is ready to use.\n"
|
||||
echo "Please visit http://<Your Public IP Address>:3000/ to get started."
|
||||
echo "It will take a few minutes to start up, don't worry."
|
||||
@@ -1,114 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
clear
|
||||
ARG1=$1
|
||||
WHO=$(whoami)
|
||||
APP_ID=$(cat /proc/sys/kernel/random/uuid)
|
||||
RANDOM_SECRET=$(echo $(($(date +%s%N) / 1000000)) | sha256sum | base64 | head -c 32)
|
||||
SENTRY_DSN="https://9e7a74326f29422584d2d0bebdc8b7d3@o1082494.ingest.sentry.io/6091062"
|
||||
|
||||
UBUNTU_MAJOR_MIN=20
|
||||
UBUNTU_MINOR_MIN=04
|
||||
OS_OK="nok"
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
if [ $ARG1 ] && [ $ARG1 == "-d" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
function errorchecker() {
|
||||
exitCode=$?
|
||||
if [ $exitCode -ne "0" ]; then
|
||||
echo "$0 exited unexpectedly with status: $exitCode"
|
||||
exit $exitCode
|
||||
fi
|
||||
}
|
||||
trap 'errorchecker' EXIT
|
||||
|
||||
if [ $WHO != 'root' ]; then
|
||||
echo 'Run as root please: sudo sh -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)"'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. /etc/lsb-release
|
||||
if [ $DISTRIB_ID != 'Ubuntu' ]; then
|
||||
echo 'Not supported OS, please open an issue on Github to get supported version.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISTRIB_RELEASE_MAJOR=$(echo "$DISTRIB_RELEASE" | cut -d'.' -f 1)
|
||||
DISTRIB_RELEASE_MINOR=$(echo "$DISTRIB_RELEASE" | cut -d'.' -f 2)
|
||||
|
||||
if [ "$DISTRIB_RELEASE_MAJOR" -ge "$UBUNTU_MAJOR_MIN" ] &&
|
||||
[ "$DISTRIB_RELEASE_MINOR" -ge "$UBUNTU_MINOR_MIN" ]; then
|
||||
OS_OK="ok"
|
||||
fi
|
||||
|
||||
if [ $OS_OK == 'nok' ]; then
|
||||
echo "Ubuntu version less than $UBUNTU_MAJOR_MIN.$UBUNTU_MINOR_MIN."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function installPodman() {
|
||||
apt-get update -y
|
||||
apt-get install curl wget gnupg2 -y
|
||||
if [ "$DISTRIB_RELEASE_MAJOR" -eq "20" ] && [ "$DISTRIB_RELEASE_MINOR" -eq "04" ]; then
|
||||
echo 'Installing on 20.04'
|
||||
source /etc/os-release
|
||||
sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
|
||||
wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_${VERSION_ID}/Release.key -O- | apt-key add -
|
||||
apt-get update -y
|
||||
apt-get -y install podman
|
||||
return 0
|
||||
elif [ "$DISTRIB_RELEASE_MAJOR" -eq "20" ] && [ "$DISTRIB_RELEASE_MINOR" -eq "10" ]; then
|
||||
apt-get -y install podman
|
||||
return 0
|
||||
elif [ "$DISTRIB_RELEASE_MAJOR" -gt "20" ]; then
|
||||
apt-get -y install podman
|
||||
return 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
if [ ! -x "$(command -v podman)" ]; then
|
||||
while true; do
|
||||
read -p "Podman not found, should I install it automatically? [Yy/Nn] " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
installPodman
|
||||
break
|
||||
;;
|
||||
[Nn]*)
|
||||
echo "Please install docker manually and update it to the latest, but at least to $DOCKER_MAJOR.$DOCKER_MINOR"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Please answer Yy or Nn." ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
# Making base directory for coolify
|
||||
if [ ! -d coolify ]; then
|
||||
mkdir coolify
|
||||
fi
|
||||
|
||||
echo "COOLIFY_APP_ID=$APP_ID
|
||||
COOLIFY_SECRET_KEY=$RANDOM_SECRET
|
||||
COOLIFY_DATABASE_URL=file:../db/prod.db
|
||||
COOLIFY_SENTRY_DSN=$SENTRY_DSN
|
||||
COOLIFY_HOSTED_ON=docker" >coolify/.env
|
||||
|
||||
systemctl start podman.socket
|
||||
systemctl enable podman.socket
|
||||
|
||||
podman volume create coolify-db
|
||||
podman volume create coolify-ssl-certs
|
||||
podman volume create coolify-letsencrypt
|
||||
|
||||
|
||||
cd coolify && podman run --privileged -tid --env-file .env -v /var/run/podman/podman.sock:/var/run/podman/podman.sock -v coolify-db-sqlite:/app/db docker.io/coollabsio/coolify:latest /bin/sh -c "env | grep COOLIFY > .env && docker-compose up -d --force-recreate"
|
||||
echo "Done"
|
||||
exit 0
|
||||
@@ -7,6 +7,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${imageforBuild}`);
|
||||
Dockerfile.push('WORKDIR /usr/share/nginx/html');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]');
|
||||
|
||||
@@ -10,6 +10,8 @@ import nuxtjs from './nuxtjs';
|
||||
import vuejs from './vuejs';
|
||||
import php from './php';
|
||||
import rust from './rust';
|
||||
import astro from './static';
|
||||
import eleventy from './static';
|
||||
|
||||
export {
|
||||
node,
|
||||
@@ -23,5 +25,7 @@ export {
|
||||
nuxtjs,
|
||||
vuejs,
|
||||
php,
|
||||
rust
|
||||
rust,
|
||||
astro,
|
||||
eleventy
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(
|
||||
`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${baseDirectory || ''} ./`
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
|
||||
@@ -16,6 +16,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
|
||||
@@ -16,6 +16,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
|
||||
@@ -6,6 +6,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push('RUN a2enmod rewrite');
|
||||
Dockerfile.push('WORKDIR /var/www/html');
|
||||
Dockerfile.push(`COPY ./${baseDirectory || ''} /var/www/html`);
|
||||
|
||||
@@ -6,6 +6,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
const Dockerfile: Array<string> = [];
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push('WORKDIR /usr/share/nginx/html');
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
|
||||
@@ -8,6 +8,7 @@ const createDockerfile = async (data, image, name): Promise<void> => {
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/target target`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/local/cargo /usr/local/cargo`);
|
||||
Dockerfile.push(`COPY . .`);
|
||||
|
||||
@@ -16,6 +16,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/share/nginx/html');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
|
||||
@@ -7,6 +7,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/share/nginx/html');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]');
|
||||
|
||||
@@ -7,6 +7,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /usr/share/nginx/html');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/src/app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
Dockerfile.push('CMD ["nginx", "-g", "daemon off;"]');
|
||||
|
||||
@@ -103,9 +103,14 @@ export const getUserDetails = async (event, isAdminRequired = true) => {
|
||||
};
|
||||
|
||||
export function getEngine(engine) {
|
||||
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : `tcp://${engine}:2375`;
|
||||
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : engine;
|
||||
}
|
||||
|
||||
// export async function saveSshKey(destination) {
|
||||
// return await asyncExecShell(
|
||||
// `echo '${destination.sshPrivateKey}' > /tmp/id_rsa_${destination.id} && chmod 600 /tmp/id_rsa_${destination.id}`
|
||||
// );
|
||||
// }
|
||||
export async function removeContainer(id, engine) {
|
||||
const host = getEngine(engine);
|
||||
try {
|
||||
|
||||
@@ -15,16 +15,12 @@
|
||||
export let placeholder = '';
|
||||
|
||||
let disabledClass = 'bg-coolback disabled:bg-coolblack';
|
||||
let actionsShow = false;
|
||||
let isHttps = browser && window.location.protocol === 'https:';
|
||||
|
||||
function showActions(value) {
|
||||
actionsShow = value;
|
||||
}
|
||||
function copyToClipboard() {
|
||||
if (isHttps && navigator.clipboard) {
|
||||
navigator.clipboard.writeText(value);
|
||||
toast.push('Copied to clipboard');
|
||||
toast.push('Copied to clipboard.');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -33,8 +29,10 @@
|
||||
{#if !isPasswordField || showPassword}
|
||||
{#if textarea}
|
||||
<textarea
|
||||
rows="3"
|
||||
rows="5"
|
||||
class={disabledClass}
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
{placeholder}
|
||||
type="text"
|
||||
{id}
|
||||
@@ -48,6 +46,8 @@
|
||||
<input
|
||||
class={disabledClass}
|
||||
type="text"
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
{id}
|
||||
{name}
|
||||
{required}
|
||||
@@ -61,6 +61,8 @@
|
||||
{:else}
|
||||
<input
|
||||
class={disabledClass}
|
||||
class:pr-10={true}
|
||||
class:pr-20={value && isHttps}
|
||||
type="password"
|
||||
{id}
|
||||
{name}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
export let customClass = 'max-w-[24rem]';
|
||||
</script>
|
||||
|
||||
<div class="py-1 text-xs text-stone-400 {customClass}">{@html text}</div>
|
||||
<div class="p-2 text-xs text-stone-400 {customClass}">{@html text}</div>
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
<Explainer text={description} />
|
||||
</div>
|
||||
</div>
|
||||
<div class:tooltip={dataTooltip} class:text-center={isCenter} data-tooltip={dataTooltip}>
|
||||
<div
|
||||
class:tooltip={dataTooltip}
|
||||
class:text-center={isCenter}
|
||||
data-tooltip={dataTooltip}
|
||||
class="flex justify-center"
|
||||
>
|
||||
<div
|
||||
type="button"
|
||||
on:click
|
||||
|
||||
@@ -15,3 +15,6 @@ export const notNodeDeployments = ['php', 'docker', 'rust'];
|
||||
export function getDomain(domain) {
|
||||
return domain?.replace('https://', '').replace('http://', '');
|
||||
}
|
||||
export function generateRemoteEngine(destination) {
|
||||
return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`;
|
||||
}
|
||||
|
||||
21
src/lib/components/svg/applications/Astro.svelte
Normal file
21
src/lib/components/svg/applications/Astro.svelte
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg
|
||||
class="absolute top-0 left-0 -m-6 h-14 w-14"
|
||||
viewBox="0 0 256 256"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
id="a"
|
||||
fill="#302649"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||
/>
|
||||
<path
|
||||
id="flame"
|
||||
fill="#EF661E"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
6
src/lib/components/svg/applications/Eleventy.svelte
Normal file
6
src/lib/components/svg/applications/Eleventy.svelte
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 128 128" class="absolute top-0 left-0 -m-8 h-16 w-16">
|
||||
<path fill="transparent" d="M18 0h92v128H18z" /><path
|
||||
d="M55.3 36.3h.4c1.1 0 1.5.9 1.5 2.3v41.8c0 1.8-.4 3-1.6 3l-4.8-.1c-1.2 0-1.6-1-1.6-3V45.5l-2.1.5c-1 0-1.5-.8-1.5-2.2v-3c0-1.2.4-2 1.4-2.2l8.3-2.2zm16 36.1l.1 3 .6 1.3.6.6.8.1h2.2c1 0 1.7.8 1.7 2v1.9c0 1.2-.6 2-1.8 2h-3.2l-2.3-.1c-.7-.2-1.4-.5-2.2-1a5.7 5.7 0 01-2-1.9c-.4-.8-.8-1.9-1-3.2-.4-1.4-.5-3-.5-4.8v-16h-1.5c-1.1 0-1.6-1-1.6-2.4v-1.7c0-1.4.5-2.3 1.6-2.3h1.5v-.1l.6-12.3c0-1.5.5-2.5 1.6-2.5h3.1c1.2 0 1.6 1 1.6 2.5v12.3h3.6c1.1 0 1.6 1 1.6 2.4V54c0 1.4-.5 2.3-1.6 2.3h-3.6v16.2zm9.4 15.7c0-2 .3-3.2 1.5-3.2.2 0 .4 0 1.4.4l1.1.3.2-.1.4-.7c.3-.6.4-1.6.4-3l-.6-3.3L79 52.7v-.9c0-1.2.3-2 1.2-2H84c.5 0 1 .3 1.3.6.3.4.5.9.6 1.6l3.4 18 2.6-17.8c.1-.8.3-1.3.6-1.7.3-.4.8-.6 1.3-.6h2.6c1 0 1.4.8 1.4 2l-.1.8L92 82.2c-.5 2.5-1 4.4-1.5 5.7a6.6 6.6 0 01-2 3c-.9.6-1.9.9-3.1.9h-.3c-2 0-3.3-.6-4.1-1.7-.3-.4-.4-1-.4-2zM31.3 38.8l8.2-2.1h.5c1 0 1.4.8 1.4 2.2v41.9c0 1.8-.4 2.9-1.6 2.9h-4.7c-1.2 0-1.6-1.1-1.6-3v-35l-2 .6c-1.2 0-1.6-.9-1.6-2.2v-3c0-1.2.4-2 1.4-2.3z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,126 +1,263 @@
|
||||
const defaultBuildAndDeploy = {
|
||||
installCommand: 'yarn install',
|
||||
buildCommand: 'yarn build',
|
||||
startCommand: 'yarn start'
|
||||
};
|
||||
export const buildPacks = [
|
||||
{
|
||||
function defaultBuildAndDeploy(packageManager) {
|
||||
return {
|
||||
installCommand:
|
||||
packageManager === 'npm' ? `${packageManager} install` : `${packageManager} install`,
|
||||
buildCommand:
|
||||
packageManager === 'npm' ? `${packageManager} run build` : `${packageManager} build`,
|
||||
startCommand:
|
||||
packageManager === 'npm' ? `${packageManager} run start` : `${packageManager} start`
|
||||
};
|
||||
}
|
||||
export function findBuildPack(pack, packageManager = 'npm') {
|
||||
const metaData = buildPacks.find((b) => b.name === pack);
|
||||
if (pack === 'node') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: null
|
||||
};
|
||||
}
|
||||
if (pack === 'static') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'docker') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: null
|
||||
};
|
||||
}
|
||||
if (pack === 'svelte') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: 'public',
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'nestjs') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
startCommand:
|
||||
packageManager === 'npm' ? 'npm run start:prod' : `${packageManager} run start:prod`,
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
};
|
||||
}
|
||||
if (pack === 'react') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: 'build',
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'nextjs') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
};
|
||||
}
|
||||
if (pack === 'gatsby') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: 'public',
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'vuejs') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: 'dist',
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'nuxtjs') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
};
|
||||
}
|
||||
if (pack === 'preact') {
|
||||
return {
|
||||
...metaData,
|
||||
...defaultBuildAndDeploy(packageManager),
|
||||
publishDirectory: 'build',
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'php') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'rust') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: 3000
|
||||
};
|
||||
}
|
||||
if (pack === 'astro') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: `yarn install`,
|
||||
buildCommand: `yarn build`,
|
||||
startCommand: null,
|
||||
publishDirectory: `dist`,
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
if (pack === 'eleventy') {
|
||||
return {
|
||||
...metaData,
|
||||
installCommand: `yarn install`,
|
||||
buildCommand: `yarn build`,
|
||||
startCommand: null,
|
||||
publishDirectory: `_site`,
|
||||
port: 80
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: 'node',
|
||||
fancyName: 'Node.js',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700',
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: null,
|
||||
port: null
|
||||
};
|
||||
}
|
||||
export const buildPacks = [
|
||||
{
|
||||
name: 'node',
|
||||
fancyName: 'Node.js',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'static',
|
||||
publishDirectory: 'dist',
|
||||
port: 80,
|
||||
fancyName: 'Static',
|
||||
hoverColor: 'hover:bg-orange-700',
|
||||
color: 'bg-orange-700'
|
||||
},
|
||||
{
|
||||
name: 'docker',
|
||||
installCommand: null,
|
||||
buildCommand: null,
|
||||
startCommand: null,
|
||||
publishDirectory: null,
|
||||
port: null,
|
||||
fancyName: 'Docker',
|
||||
hoverColor: 'hover:bg-sky-700',
|
||||
color: 'bg-sky-700'
|
||||
},
|
||||
{
|
||||
name: 'php',
|
||||
fancyName: 'PHP',
|
||||
hoverColor: 'hover:bg-indigo-700',
|
||||
color: 'bg-indigo-700'
|
||||
},
|
||||
{
|
||||
name: 'svelte',
|
||||
...defaultBuildAndDeploy,
|
||||
publishDirectory: 'public',
|
||||
port: 80,
|
||||
fancyName: 'Svelte',
|
||||
hoverColor: 'hover:bg-orange-700',
|
||||
color: 'bg-orange-700'
|
||||
},
|
||||
{
|
||||
name: 'nestjs',
|
||||
...defaultBuildAndDeploy,
|
||||
startCommand: 'yarn start:prod',
|
||||
port: 3000,
|
||||
fancyName: 'NestJS',
|
||||
hoverColor: 'hover:bg-red-700',
|
||||
color: 'bg-red-700'
|
||||
},
|
||||
{
|
||||
name: 'react',
|
||||
...defaultBuildAndDeploy,
|
||||
publishDirectory: 'build',
|
||||
port: 80,
|
||||
fancyName: 'React',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'nextjs',
|
||||
...defaultBuildAndDeploy,
|
||||
port: 3000,
|
||||
fancyName: 'NextJS',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'gatsby',
|
||||
...defaultBuildAndDeploy,
|
||||
publishDirectory: 'public',
|
||||
port: 80,
|
||||
fancyName: 'Gatsby',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'vuejs',
|
||||
...defaultBuildAndDeploy,
|
||||
publishDirectory: 'dist',
|
||||
port: 80,
|
||||
fancyName: 'VueJS',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700'
|
||||
},
|
||||
{
|
||||
name: 'nuxtjs',
|
||||
...defaultBuildAndDeploy,
|
||||
port: 3000,
|
||||
fancyName: 'NuxtJS',
|
||||
hoverColor: 'hover:bg-green-700',
|
||||
color: 'bg-green-700'
|
||||
},
|
||||
{
|
||||
name: 'gatsby',
|
||||
fancyName: 'Gatsby',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'astro',
|
||||
fancyName: 'Astro',
|
||||
hoverColor: 'hover:bg-pink-700',
|
||||
color: 'bg-pink-700'
|
||||
},
|
||||
{
|
||||
name: 'eleventy',
|
||||
fancyName: 'Eleventy',
|
||||
hoverColor: 'hover:bg-red-700',
|
||||
color: 'bg-red-700'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'react',
|
||||
fancyName: 'React',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'preact',
|
||||
...defaultBuildAndDeploy,
|
||||
publishDirectory: 'build',
|
||||
port: 80,
|
||||
fancyName: 'Preact',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'php',
|
||||
port: 80,
|
||||
fancyName: 'PHP',
|
||||
hoverColor: 'hover:bg-indigo-700',
|
||||
color: 'bg-indigo-700'
|
||||
name: 'nextjs',
|
||||
fancyName: 'NextJS',
|
||||
hoverColor: 'hover:bg-blue-700',
|
||||
color: 'bg-blue-700'
|
||||
},
|
||||
{
|
||||
name: 'nestjs',
|
||||
fancyName: 'NestJS',
|
||||
hoverColor: 'hover:bg-red-700',
|
||||
color: 'bg-red-700'
|
||||
},
|
||||
{
|
||||
name: 'rust',
|
||||
port: 3000,
|
||||
fancyName: 'Rust',
|
||||
hoverColor: 'hover:bg-pink-700',
|
||||
color: 'bg-pink-700'
|
||||
}
|
||||
];
|
||||
export const scanningTemplates = {
|
||||
astro: {
|
||||
buildPack: 'astro'
|
||||
},
|
||||
'@11ty/eleventy': {
|
||||
buildPack: 'eleventy'
|
||||
},
|
||||
svelte: {
|
||||
buildPack: 'svelte'
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { removeProxyConfiguration } from '$lib/haproxy';
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
|
||||
import { getDomain, removeDestinationDocker } from '$lib/common';
|
||||
@@ -119,7 +118,8 @@ export async function getApplicationWebhook({ projectId, branch }) {
|
||||
}
|
||||
export async function getApplicationById({ id }) {
|
||||
const body = await prisma.application.findFirst({
|
||||
where: { id }
|
||||
where: { id },
|
||||
include: { destinationDocker: true }
|
||||
});
|
||||
|
||||
return { ...body };
|
||||
@@ -135,13 +135,13 @@ export async function getApplication({ id, teamId }) {
|
||||
}
|
||||
});
|
||||
|
||||
if (body.gitSource?.githubApp?.clientSecret) {
|
||||
if (body?.gitSource?.githubApp?.clientSecret) {
|
||||
body.gitSource.githubApp.clientSecret = decrypt(body.gitSource.githubApp.clientSecret);
|
||||
}
|
||||
if (body.gitSource?.githubApp?.webhookSecret) {
|
||||
if (body?.gitSource?.githubApp?.webhookSecret) {
|
||||
body.gitSource.githubApp.webhookSecret = decrypt(body.gitSource.githubApp.webhookSecret);
|
||||
}
|
||||
if (body.gitSource?.githubApp?.privateKey) {
|
||||
if (body?.gitSource?.githubApp?.privateKey) {
|
||||
body.gitSource.githubApp.privateKey = decrypt(body.gitSource.githubApp.privateKey);
|
||||
}
|
||||
if (body?.gitSource?.gitlabApp?.appSecret) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import * as db from '$lib/database';
|
||||
import cuid from 'cuid';
|
||||
import { generatePassword } from '.';
|
||||
import { prisma, ErrorHandler } from './common';
|
||||
import getPort from 'get-port';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
|
||||
|
||||
export async function listDatabases(teamId) {
|
||||
@@ -16,24 +16,9 @@ export async function newDatabase({ name, teamId }) {
|
||||
const rootUserPassword = encrypt(generatePassword());
|
||||
const defaultDatabase = cuid();
|
||||
|
||||
let publicPort = await getPort();
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
const usedPorts = await prisma.database.findMany({ where: { publicPort } });
|
||||
if (usedPorts.length === 0) break;
|
||||
publicPort = await getPort();
|
||||
i++;
|
||||
} while (i < 10);
|
||||
if (i === 9) {
|
||||
throw {
|
||||
error: 'No free port found!? Is it possible?'
|
||||
};
|
||||
}
|
||||
return await prisma.database.create({
|
||||
data: {
|
||||
name,
|
||||
publicPort,
|
||||
defaultDatabase,
|
||||
dbUser,
|
||||
dbUserPassword,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { startCoolifyProxy } from '$lib/haproxy';
|
||||
import { getDatabaseImage } from '.';
|
||||
@@ -47,7 +48,36 @@ export async function updateDestination({ id, name, engine, network }) {
|
||||
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
||||
}
|
||||
|
||||
export async function newDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
||||
export async function newRemoteDestination({
|
||||
name,
|
||||
teamId,
|
||||
engine,
|
||||
network,
|
||||
isCoolifyProxyUsed,
|
||||
remoteEngine,
|
||||
ipAddress,
|
||||
user,
|
||||
port,
|
||||
sshPrivateKey
|
||||
}) {
|
||||
const encryptedPrivateKey = encrypt(sshPrivateKey);
|
||||
const destination = await prisma.destinationDocker.create({
|
||||
data: {
|
||||
name,
|
||||
teams: { connect: { id: teamId } },
|
||||
engine,
|
||||
network,
|
||||
isCoolifyProxyUsed,
|
||||
remoteEngine,
|
||||
ipAddress,
|
||||
user,
|
||||
port,
|
||||
sshPrivateKey: encryptedPrivateKey
|
||||
}
|
||||
});
|
||||
return destination.id;
|
||||
}
|
||||
export async function newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
||||
const host = getEngine(engine);
|
||||
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
||||
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
||||
@@ -94,9 +124,13 @@ export async function removeDestination({ id }) {
|
||||
}
|
||||
|
||||
export async function getDestination({ id, teamId }) {
|
||||
return await prisma.destinationDocker.findFirst({
|
||||
let destination = await prisma.destinationDocker.findFirst({
|
||||
where: { id, teams: { some: { id: teamId } } }
|
||||
});
|
||||
if (destination.remoteEngine) {
|
||||
destination.sshPrivateKey = decrypt(destination.sshPrivateKey);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
export async function getDestinationByApplicationId({ id, teamId }) {
|
||||
return await prisma.destinationDocker.findFirst({
|
||||
|
||||
@@ -10,13 +10,18 @@ export async function hashPassword(password: string) {
|
||||
const saltRounds = 15;
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
export async function login({ email, password }) {
|
||||
export async function login({ email, password, isLogin }) {
|
||||
const users = await prisma.user.count();
|
||||
const userFound = await prisma.user.findUnique({
|
||||
where: { email },
|
||||
include: { teams: true, permission: true },
|
||||
rejectOnNotFound: false
|
||||
});
|
||||
if (!userFound && isLogin) {
|
||||
throw {
|
||||
error: 'Wrong password or email address.'
|
||||
};
|
||||
}
|
||||
// Registration disabled if database is not seeded properly
|
||||
const { isRegistrationEnabled, id } = await db.listSettings();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push('WORKDIR /usr/src/app');
|
||||
Dockerfile.push(`LABEL coolify.image=true`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
|
||||
259
src/lib/haproxy/configuration.ts
Normal file
259
src/lib/haproxy/configuration.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import { dev } from '$app/env';
|
||||
import got from 'got';
|
||||
import mustache from 'mustache';
|
||||
import crypto from 'crypto';
|
||||
|
||||
import * as db from '$lib/database';
|
||||
import { checkContainer, checkHAProxy } from '.';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
|
||||
const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555';
|
||||
|
||||
let template = `program api
|
||||
command /usr/bin/dataplaneapi -f /usr/local/etc/haproxy/dataplaneapi.hcl --userlist haproxy-dataplaneapi
|
||||
no option start-on-reload
|
||||
|
||||
global
|
||||
stats socket /var/run/api.sock user haproxy group haproxy mode 660 level admin expose-fd listeners
|
||||
log stdout format raw local0 debug
|
||||
|
||||
defaults
|
||||
mode http
|
||||
log global
|
||||
timeout http-request 60s
|
||||
timeout connect 10s
|
||||
timeout client 60s
|
||||
timeout server 60s
|
||||
|
||||
userlist haproxy-dataplaneapi
|
||||
user admin insecure-password "\${HAPROXY_PASSWORD}"
|
||||
|
||||
frontend http
|
||||
mode http
|
||||
bind :80
|
||||
bind :443 ssl crt /usr/local/etc/haproxy/ssl/ alpn h2,http/1.1
|
||||
acl is_certbot path_beg /.well-known/acme-challenge/
|
||||
{{#applications}}
|
||||
{{#isHttps}}
|
||||
http-request redirect scheme https code ${
|
||||
dev ? 302 : 301
|
||||
} if { hdr(host) -i {{domain}} } !{ ssl_fc }
|
||||
{{/isHttps}}
|
||||
http-request redirect location {{{redirectValue}}} code ${
|
||||
dev ? 302 : 301
|
||||
} if { req.hdr(host) -i {{redirectTo}} }
|
||||
{{/applications}}
|
||||
{{#services}}
|
||||
{{#isHttps}}
|
||||
http-request redirect scheme https code ${
|
||||
dev ? 302 : 301
|
||||
} if { hdr(host) -i {{domain}} } !{ ssl_fc }
|
||||
{{/isHttps}}
|
||||
http-request redirect location {{{redirectValue}}} code ${
|
||||
dev ? 302 : 301
|
||||
} if { req.hdr(host) -i {{redirectTo}} }
|
||||
{{/services}}
|
||||
{{#coolify}}
|
||||
{{#isHttps}}
|
||||
http-request redirect scheme https code ${
|
||||
dev ? 302 : 301
|
||||
} if { hdr(host) -i {{domain}} } !{ ssl_fc }
|
||||
{{/isHttps}}
|
||||
http-request redirect location {{{redirectValue}}} code ${
|
||||
dev ? 302 : 301
|
||||
} if { req.hdr(host) -i {{redirectTo}} }
|
||||
{{/coolify}}
|
||||
use_backend backend-certbot if is_certbot
|
||||
use_backend %[req.hdr(host),lower]
|
||||
|
||||
frontend stats
|
||||
bind *:8404
|
||||
stats enable
|
||||
stats uri /
|
||||
stats admin if TRUE
|
||||
stats auth "\${HAPROXY_USERNAME}:\${HAPROXY_PASSWORD}"
|
||||
|
||||
backend backend-certbot
|
||||
mode http
|
||||
server certbot host.docker.internal:9080
|
||||
|
||||
{{#applications}}
|
||||
{{#isRunning}}
|
||||
# updatedAt={{updatedAt}}
|
||||
backend {{domain}}
|
||||
option forwardfor
|
||||
server {{id}} {{id}}:{{port}} check
|
||||
{{/isRunning}}
|
||||
{{/applications}}
|
||||
|
||||
{{#services}}
|
||||
{{#isRunning}}
|
||||
# updatedAt={{updatedAt}}
|
||||
backend {{domain}}
|
||||
option forwardfor
|
||||
server {{id}} {{id}}:{{port}} check
|
||||
{{/isRunning}}
|
||||
{{/services}}
|
||||
|
||||
{{#coolify}}
|
||||
backend {{domain}}
|
||||
option forwardfor
|
||||
option httpchk GET /undead.json
|
||||
server {{id}} {{id}}:{{port}} check fall 10
|
||||
{{/coolify}}
|
||||
`;
|
||||
export async function haproxyInstance() {
|
||||
const { proxyPassword } = await db.listSettings();
|
||||
return got.extend({
|
||||
prefixUrl: url,
|
||||
username: 'admin',
|
||||
password: proxyPassword
|
||||
});
|
||||
}
|
||||
|
||||
export async function configureHAProxy() {
|
||||
try {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
const data = {
|
||||
applications: [],
|
||||
services: [],
|
||||
coolify: []
|
||||
};
|
||||
const applications = await db.prisma.application.findMany({
|
||||
include: { destinationDocker: true, settings: true }
|
||||
});
|
||||
for (const application of applications) {
|
||||
const {
|
||||
fqdn,
|
||||
id,
|
||||
port,
|
||||
destinationDocker,
|
||||
destinationDockerId,
|
||||
settings: { previews },
|
||||
updatedAt
|
||||
} = application;
|
||||
if (destinationDockerId) {
|
||||
const { engine, network } = destinationDocker;
|
||||
const isRunning = await checkContainer(engine, id);
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
if (isRunning) {
|
||||
data.applications.push({
|
||||
id,
|
||||
port: port || 3000,
|
||||
domain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? domain : 'www.' + domain,
|
||||
updatedAt: updatedAt.getTime()
|
||||
});
|
||||
}
|
||||
if (previews) {
|
||||
const host = getEngine(engine);
|
||||
const { stdout } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"`
|
||||
);
|
||||
const containers = stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((a) => a)
|
||||
.map((c) => c.replace(/"/g, ''));
|
||||
if (containers.length > 0) {
|
||||
for (const container of containers) {
|
||||
let previewDomain = `${container.split('-')[1]}.${domain}`;
|
||||
data.applications.push({
|
||||
id: container,
|
||||
port: port || 3000,
|
||||
domain: previewDomain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? previewDomain : 'www.' + previewDomain,
|
||||
updatedAt: updatedAt.getTime()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const services = await db.prisma.service.findMany({
|
||||
include: {
|
||||
destinationDocker: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true
|
||||
}
|
||||
});
|
||||
|
||||
for (const service of services) {
|
||||
const { fqdn, id, type, destinationDocker, destinationDockerId, updatedAt } = service;
|
||||
if (destinationDockerId) {
|
||||
const { engine } = destinationDocker;
|
||||
const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||
if (found) {
|
||||
const port = found.ports.main;
|
||||
const publicPort = service[type]?.publicPort;
|
||||
const isRunning = await checkContainer(engine, id);
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
if (isRunning) {
|
||||
data.services.push({
|
||||
id,
|
||||
port,
|
||||
publicPort,
|
||||
domain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? domain : 'www.' + domain,
|
||||
updatedAt: updatedAt.getTime()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const { fqdn } = await db.prisma.setting.findFirst();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`;
|
||||
data.coolify.push({
|
||||
id: dev ? 'host.docker.internal' : 'coolify',
|
||||
port: 3000,
|
||||
domain,
|
||||
isHttps,
|
||||
redirectValue,
|
||||
redirectTo: isWWW ? domain : 'www.' + domain
|
||||
});
|
||||
}
|
||||
const output = mustache.render(template, data);
|
||||
const newHash = crypto.createHash('md5').update(output).digest('hex');
|
||||
const { proxyHash, id } = await db.listSettings();
|
||||
if (proxyHash !== newHash) {
|
||||
await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } });
|
||||
await haproxy.post(`v2/services/haproxy/configuration/raw`, {
|
||||
searchParams: {
|
||||
skip_version: true
|
||||
},
|
||||
body: output,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { dev } from '$app/env';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { asyncExecShell, getEngine } from '$lib/common';
|
||||
import got from 'got';
|
||||
import * as db from '$lib/database';
|
||||
|
||||
@@ -47,113 +47,6 @@ export async function completeTransaction(transactionId) {
|
||||
const haproxy = await haproxyInstance();
|
||||
return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`);
|
||||
}
|
||||
|
||||
export async function removeProxyConfiguration(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const haproxy = await haproxyInstance();
|
||||
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: {
|
||||
transaction_id: transactionId
|
||||
}
|
||||
})
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
await forceSSLOffApplication(domain);
|
||||
await removeWwwRedirection(fqdn);
|
||||
}
|
||||
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 {
|
||||
if (transactionId) 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;
|
||||
}
|
||||
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();
|
||||
await checkHAProxy(haproxy);
|
||||
@@ -177,7 +70,7 @@ export async function deleteProxy({ id }) {
|
||||
})
|
||||
.json();
|
||||
} catch (error) {
|
||||
console.log(error.response.body);
|
||||
console.log(error.response?.body || error);
|
||||
} finally {
|
||||
if (transactionId) await completeTransaction(transactionId);
|
||||
}
|
||||
@@ -187,109 +80,6 @@ export async function reloadHaproxy(engine) {
|
||||
const host = getEngine(engine);
|
||||
return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`);
|
||||
}
|
||||
export async function configureProxyForApplication({ domain, imageId, applicationId, port }) {
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
|
||||
let serverConfigured = false;
|
||||
let backendAvailable: any = null;
|
||||
|
||||
try {
|
||||
backendAvailable = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/backends/${domain}`)
|
||||
.json();
|
||||
const server: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/servers/${imageId}`, {
|
||||
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 === imageId) {
|
||||
if (server.data.port === port) {
|
||||
serverConfigured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log('error getting backend or server', error?.response?.body);
|
||||
//
|
||||
}
|
||||
|
||||
if (serverConfigured) return;
|
||||
const transactionId = await getNextTransactionId();
|
||||
if (backendAvailable) {
|
||||
await haproxy
|
||||
.delete(`v2/services/haproxy/configuration/backends/${domain}`, {
|
||||
searchParams: {
|
||||
transaction_id: transactionId
|
||||
}
|
||||
})
|
||||
.json();
|
||||
}
|
||||
try {
|
||||
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: imageId,
|
||||
check: 'enabled',
|
||||
name: imageId,
|
||||
port: port
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw error?.response?.body || error;
|
||||
} finally {
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function configureCoolifyProxyOff(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
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: {
|
||||
transaction_id: transactionId
|
||||
}
|
||||
})
|
||||
.json();
|
||||
await completeTransaction(transactionId);
|
||||
if (isHttps) await forceSSLOffApplication(domain);
|
||||
await removeWwwRedirection(fqdn);
|
||||
} catch (error) {
|
||||
throw error?.response?.body || error;
|
||||
}
|
||||
}
|
||||
export async function checkHAProxy(haproxy?: any) {
|
||||
if (!haproxy) haproxy = await haproxyInstance();
|
||||
try {
|
||||
@@ -301,76 +91,6 @@ export async function checkHAProxy(haproxy?: any) {
|
||||
};
|
||||
}
|
||||
}
|
||||
export async function configureCoolifyProxyOn(fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const haproxy = await haproxyInstance();
|
||||
await checkHAProxy(haproxy);
|
||||
let serverConfigured = false;
|
||||
let backendAvailable: any = null;
|
||||
try {
|
||||
backendAvailable = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/backends/${domain}`)
|
||||
.json();
|
||||
const server: any = await haproxy
|
||||
.get(`v2/services/haproxy/configuration/servers/coolify`, {
|
||||
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 === dev ? 'host.docker.internal' : 'coolify') {
|
||||
if (server.data.port === 3000) {
|
||||
serverConfigured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
if (serverConfigured) return;
|
||||
const transactionId = await getNextTransactionId();
|
||||
try {
|
||||
await haproxy.post('v2/services/haproxy/configuration/backends', {
|
||||
searchParams: {
|
||||
transaction_id: transactionId
|
||||
},
|
||||
json: {
|
||||
adv_check: 'httpchk',
|
||||
httpchk_params: {
|
||||
method: 'GET',
|
||||
uri: '/undead.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: dev ? 'host.docker.internal' : 'coolify',
|
||||
check: 'enabled',
|
||||
fall: 10,
|
||||
name: 'coolify',
|
||||
port: 3000
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
} finally {
|
||||
await completeTransaction(transactionId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopTcpHttpProxy(destinationDocker, publicPort) {
|
||||
const { engine } = destinationDocker;
|
||||
@@ -434,7 +154,7 @@ export async function startHttpProxy(destinationDocker, id, publicPort, privateP
|
||||
export async function startCoolifyProxy(engine) {
|
||||
const host = getEngine(engine);
|
||||
const found = await checkContainer(engine, 'coolify-haproxy');
|
||||
const { proxyPassword, proxyUser } = await db.listSettings();
|
||||
const { proxyPassword, proxyUser, id } = await db.listSettings();
|
||||
if (!found) {
|
||||
const { stdout: Config } = await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker network inspect bridge --format '{{json .IPAM.Config }}'`
|
||||
@@ -443,6 +163,7 @@ export async function startCoolifyProxy(engine) {
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST="${host}" docker run -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restart always --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' -v coolify-ssl-certs:/usr/local/etc/haproxy/ssl --network coolify-infra -p "80:80" -p "443:443" -p "8404:8404" -p "5555:5555" -p "5000:5000" --name coolify-haproxy -d coollabsio/${defaultProxyImage}`
|
||||
);
|
||||
await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
|
||||
}
|
||||
await configureNetworkCoolifyProxy(engine);
|
||||
}
|
||||
@@ -475,6 +196,8 @@ export async function stopCoolifyProxy(engine) {
|
||||
const host = getEngine(engine);
|
||||
const found = await checkContainer(engine, 'coolify-haproxy');
|
||||
await db.setDestinationSettings({ engine, isCoolifyProxyUsed: false });
|
||||
const { id } = await db.prisma.setting.findFirst({});
|
||||
await db.prisma.setting.update({ where: { id }, data: { proxyHash: null } });
|
||||
try {
|
||||
if (found) {
|
||||
await asyncExecShell(
|
||||
@@ -499,168 +222,3 @@ 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 {
|
||||
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) {}
|
||||
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(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: {
|
||||
transaction_id: transactionId
|
||||
}
|
||||
})
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default async function ({
|
||||
buildId
|
||||
}): Promise<any> {
|
||||
try {
|
||||
saveBuildLog({ line: 'GitHub importer started', buildId, applicationId });
|
||||
saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId });
|
||||
const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId });
|
||||
const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, '');
|
||||
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import { dev } from '$app/env';
|
||||
import { forceSSLOffApplication, forceSSLOnApplication } from '$lib/haproxy';
|
||||
import { asyncExecShell, getEngine } from './common';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { dev } from '$app/env';
|
||||
import cuid from 'cuid';
|
||||
import getPort from 'get-port';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
|
||||
export async function letsEncrypt({ domain, isCoolify = false, id = null }) {
|
||||
export async function letsEncrypt(domain, id = null, isCoolify = false) {
|
||||
try {
|
||||
const data = await db.prisma.setting.findFirst();
|
||||
const { minPort, maxPort } = data;
|
||||
|
||||
const nakedDomain = domain.replace('www.', '');
|
||||
const wwwDomain = `www.${nakedDomain}`;
|
||||
const randomCuid = cuid();
|
||||
const randomPort = 9080;
|
||||
const randomPort = await getPort({ port: portNumbers(minPort, maxPort) });
|
||||
|
||||
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 }
|
||||
@@ -46,7 +47,6 @@ export async function letsEncrypt({ domain, isCoolify = false, id = null }) {
|
||||
}
|
||||
}
|
||||
}
|
||||
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 ${
|
||||
@@ -70,9 +70,85 @@ export async function letsEncrypt({ domain, isCoolify = false, id = null }) {
|
||||
if (error.code !== 0) {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
if (!isCoolify) {
|
||||
await forceSSLOnApplication(domain);
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateSSLCerts() {
|
||||
const ssls = [];
|
||||
const applications = await db.prisma.application.findMany({
|
||||
include: { destinationDocker: true, settings: true }
|
||||
});
|
||||
for (const application of applications) {
|
||||
const {
|
||||
fqdn,
|
||||
id,
|
||||
destinationDocker: { engine, network },
|
||||
settings: { previews }
|
||||
} = application;
|
||||
const isRunning = await checkContainer(engine, id);
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isRunning) {
|
||||
if (isHttps) ssls.push({ domain, id, isCoolify: false });
|
||||
}
|
||||
if (previews) {
|
||||
const host = getEngine(engine);
|
||||
const { stdout } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"`
|
||||
);
|
||||
const containers = stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((a) => a)
|
||||
.map((c) => c.replace(/"/g, ''));
|
||||
if (containers.length > 0) {
|
||||
for (const container of containers) {
|
||||
let previewDomain = `${container.split('-')[1]}.${domain}`;
|
||||
if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const services = await db.prisma.service.findMany({
|
||||
include: {
|
||||
destinationDocker: true,
|
||||
minio: true,
|
||||
plausibleAnalytics: true,
|
||||
vscodeserver: true,
|
||||
wordpress: true
|
||||
}
|
||||
});
|
||||
|
||||
for (const service of services) {
|
||||
const {
|
||||
fqdn,
|
||||
id,
|
||||
type,
|
||||
destinationDocker: { engine }
|
||||
} = service;
|
||||
const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||
if (found) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isRunning = await checkContainer(engine, id);
|
||||
if (isRunning) {
|
||||
if (isHttps) ssls.push({ domain, id, isCoolify: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
const { fqdn } = await db.prisma.setting.findFirst();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true });
|
||||
}
|
||||
if (ssls.length > 0) {
|
||||
for (const ssl of ssls) {
|
||||
if (!dev) {
|
||||
await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify);
|
||||
} else {
|
||||
console.log('Generate ssl for', ssl.domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ 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, setWwwRedirection } from '../haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { decrypt } from '$lib/crypto';
|
||||
import { sentry } from '$lib/common';
|
||||
@@ -234,10 +233,16 @@ export default async function (job) {
|
||||
baseDirectory,
|
||||
publishDirectory
|
||||
});
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
|
||||
const { stderr } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker run --env-file=${workdir}/.env ${labels.join(
|
||||
`DOCKER_HOST=${host} docker run ${envFound && `--env-file=${workdir}/.env`} ${labels.join(
|
||||
' '
|
||||
)} --name ${imageId} --network ${
|
||||
docker.network
|
||||
@@ -250,25 +255,6 @@ export default async function (job) {
|
||||
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);
|
||||
}
|
||||
saveBuildLog({ line: 'Proxy will be updated shortly.', buildId, applicationId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,48 +4,57 @@ 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);
|
||||
}
|
||||
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) {
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image inspect ${image}`);
|
||||
// } catch (error) {
|
||||
// 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);
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// if (!dev) {
|
||||
// // 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 old images >3 days
|
||||
// try {
|
||||
// await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`);
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ const cron = async () => {
|
||||
);
|
||||
|
||||
await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } });
|
||||
// await queue.ssl.add('ssl', {}, { repeat: { every: 10000 } });
|
||||
if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } });
|
||||
await queue.ssl.add('ssl', {}, { repeat: { every: 60000 } });
|
||||
await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } });
|
||||
await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } });
|
||||
|
||||
const events = {
|
||||
|
||||
@@ -1,107 +1,12 @@
|
||||
import { getDomain } from '$lib/common';
|
||||
import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import {
|
||||
checkContainer,
|
||||
configureCoolifyProxyOn,
|
||||
configureProxyForApplication,
|
||||
configureSimpleServiceProxyOn,
|
||||
forceSSLOnApplication,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection,
|
||||
startCoolifyProxy,
|
||||
startHttpProxy
|
||||
} from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { dev } from '$app/env';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { configureHAProxy } from '$lib/haproxy/configuration';
|
||||
|
||||
export default async function () {
|
||||
try {
|
||||
// Check destination containers and configure proxy if needed
|
||||
const destinationDockers = await prisma.destinationDocker.findMany({});
|
||||
for (const destination of destinationDockers) {
|
||||
if (destination.isCoolifyProxyUsed) {
|
||||
const docker = dockerInstance({ destinationDocker: destination });
|
||||
const containers = await docker.engine.listContainers();
|
||||
const configurations = containers.filter(
|
||||
(container) => container.Labels['coolify.managed']
|
||||
);
|
||||
for (const configuration of configurations) {
|
||||
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);
|
||||
await startCoolifyProxy('/var/run/docker.sock');
|
||||
await configureCoolifyProxyOn(fqdn);
|
||||
await setWwwRedirection(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) await forceSSLOnApplication(domain);
|
||||
}
|
||||
return await configureHAProxy();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
console.log(error.response?.body || error);
|
||||
return ErrorHandler(error.response?.body || error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,9 @@
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { prisma } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { forceSSLOnApplication } from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { dev } from '$app/env';
|
||||
import { generateSSLCerts } from '$lib/letsencrypt';
|
||||
|
||||
export default async function () {
|
||||
try {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany({});
|
||||
for (const destination of destinationDockers) {
|
||||
if (destination.isCoolifyProxyUsed) {
|
||||
const docker = dockerInstance({ destinationDocker: destination });
|
||||
const containers = await docker.engine.listContainers();
|
||||
const configurations = containers.filter(
|
||||
(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 } = parsedConfiguration;
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) {
|
||||
if (dev) {
|
||||
console.log('DEV MODE: SSL is enabled');
|
||||
} else {
|
||||
const host = getEngine(destination.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const { fqdn } = await db.listSettings();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
if (isHttps) {
|
||||
if (dev) {
|
||||
console.log('DEV MODE: SSL is enabled');
|
||||
} else {
|
||||
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 { 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 await generateSSLCerts();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const publicPaths = [
|
||||
'/login',
|
||||
'/register',
|
||||
'/reset',
|
||||
'/reset/password',
|
||||
'/webhooks/success',
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<script>
|
||||
export let teams;
|
||||
export let selectedTeamId;
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import '../tailwind.css';
|
||||
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||
@@ -39,12 +38,13 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { asyncSleep } from '$lib/components/common';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { browser } from '$app/env';
|
||||
import { browser, dev } from '$app/env';
|
||||
|
||||
let isUpdateAvailable = false;
|
||||
|
||||
let updateStatus = {
|
||||
found: false,
|
||||
loading: false,
|
||||
checking: false,
|
||||
success: null
|
||||
};
|
||||
let latestVersion = 'latest';
|
||||
@@ -59,17 +59,20 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
if ($session.teamId === '0') {
|
||||
updateStatus.checking = true;
|
||||
try {
|
||||
const data = await get(`/update.json`);
|
||||
if (overrideVersion || data?.isUpdateAvailable) {
|
||||
latestVersion = overrideVersion || data.latestVersion;
|
||||
isUpdateAvailable = overrideVersion ? true : data?.isUpdateAvailable;
|
||||
await post(`/update.json`, { type: 'pull', latestVersion });
|
||||
console.log('checking update');
|
||||
const { exists } = await post(`/update.json`, {
|
||||
type: 'check',
|
||||
latestVersion,
|
||||
overrideVersion
|
||||
});
|
||||
isUpdateAvailable = exists;
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
updateStatus.checking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,26 +100,32 @@
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
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 {
|
||||
if (dev) {
|
||||
console.log(`updating to ${latestVersion}`);
|
||||
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();
|
||||
return window.location.reload();
|
||||
} else {
|
||||
await post(`/update.json`, { type: 'update', latestVersion });
|
||||
toast.push('Update completed.<br><br>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 }) {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
@@ -311,42 +320,17 @@
|
||||
|
||||
<div class="flex flex-col space-y-4 py-2">
|
||||
{#if $session.teamId === '0'}
|
||||
{#if updateStatus.checking}
|
||||
<button
|
||||
disabled
|
||||
in:fade={{ duration: 150 }}
|
||||
class="icons tooltip-right bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
|
||||
data-tooltip="Checking for updates..."
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-9 w-8 animate-spin"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
|
||||
<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
|
||||
<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
|
||||
<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
|
||||
<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
|
||||
<line x1="11" y1="19.94" x2="11" y2="19.95" />
|
||||
</svg></button
|
||||
>
|
||||
{:else if isUpdateAvailable}
|
||||
{#if isUpdateAvailable}
|
||||
<button
|
||||
disabled={updateStatus.success === false}
|
||||
data-tooltip="Update available"
|
||||
title="Update available"
|
||||
on:click={update}
|
||||
class="icons tooltip-right bg-gradient-to-r from-purple-500 via-pink-500 to-red-500 text-white duration-75 hover:scale-105"
|
||||
>
|
||||
{#if updateStatus.loading}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-8 h-9 lds-heart"
|
||||
class="lds-heart h-9 w-8"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -444,7 +428,8 @@
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col space-y-4 py-2">
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
href="/teams"
|
||||
@@ -519,20 +504,20 @@
|
||||
<path d="M7 12h14l-3 -3m0 6l3 -3" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-full text-center font-bold text-stone-400 hover:bg-coolgray-200 hover:text-white"
|
||||
>
|
||||
<a
|
||||
class="text-[10px] no-underline"
|
||||
href={`https://github.com/coollabsio/coolify/releases/tag/v${$session.version}`}
|
||||
target="_blank">v{$session.version}</a
|
||||
<div
|
||||
class="w-full text-center font-bold text-stone-400 hover:bg-coolgray-200 hover:text-white"
|
||||
>
|
||||
<a
|
||||
class="text-[10px] no-underline"
|
||||
href={`https://github.com/coollabsio/coolify/releases/tag/v${$session.version}`}
|
||||
target="_blank">v{$session.version}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<select
|
||||
class="fixed right-0 bottom-0 z-50 m-2 p-2 px-4"
|
||||
class="fixed right-0 bottom-0 z-50 m-2 w-64 bg-opacity-30 p-2 px-4"
|
||||
bind:value={selectedTeamId}
|
||||
on:change={switchTeam}
|
||||
>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
async function handleDeploySubmit() {
|
||||
try {
|
||||
const { buildId } = await post(`/applications/${id}/deploy.json`, { ...application });
|
||||
return await goto(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
@@ -128,7 +128,7 @@
|
||||
title="Stop application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-green-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Stop application'
|
||||
: 'You do not have permission to stop the application.'}
|
||||
@@ -153,7 +153,7 @@
|
||||
title="Rebuild application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-green-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Rebuild application'
|
||||
: 'You do not have permission to rebuild application.'}
|
||||
@@ -182,7 +182,7 @@
|
||||
title="Build and start application"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-green-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Build and start application'
|
||||
: 'You do not have permission to Build and start application.'}
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
import { asyncExecShell, getDomain, getEngine, getUserDetails } from '$lib/common';
|
||||
import { dev } from '$app/env';
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { promises as dns } from 'dns';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { fqdn } = await event.request.json();
|
||||
let { fqdn, forceSave } = await event.request.json();
|
||||
fqdn = fqdn.toLowerCase();
|
||||
|
||||
try {
|
||||
const domain = getDomain(fqdn);
|
||||
const found = await db.isDomainConfigured({ id, fqdn });
|
||||
if (found) {
|
||||
throw {
|
||||
message: `Domain ${getDomain(fqdn).replace('www.', '')} is already configured.`
|
||||
message: `Domain ${getDomain(fqdn).replace('www.', '')} is already used.`
|
||||
};
|
||||
}
|
||||
if (!dev && !forceSave) {
|
||||
let ip = [];
|
||||
let localIp = [];
|
||||
dns.setServers(['1.1.1.1', '8.8.8.8']);
|
||||
|
||||
try {
|
||||
localIp = await dns.resolve4(event.url.hostname);
|
||||
} catch (error) {}
|
||||
try {
|
||||
ip = await dns.resolve4(domain);
|
||||
} catch (error) {}
|
||||
|
||||
if (localIp?.length > 0) {
|
||||
if (ip?.length === 0 || !ip.includes(localIp[0])) {
|
||||
throw {
|
||||
message: `DNS not set or propogated for ${domain}.<br><br>Please check your DNS settings.`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import { post } from '$lib/api';
|
||||
import { findBuildPack } from '$lib/components/templates';
|
||||
import { errorNotification } from '$lib/form';
|
||||
|
||||
const { id } = $page.params;
|
||||
@@ -11,10 +12,13 @@
|
||||
export let buildPack;
|
||||
export let foundConfig;
|
||||
export let scanning;
|
||||
export let packageManager;
|
||||
|
||||
async function handleSubmit(name) {
|
||||
try {
|
||||
const tempBuildPack = JSON.parse(JSON.stringify(buildPack));
|
||||
const tempBuildPack = JSON.parse(
|
||||
JSON.stringify(findBuildPack(buildPack.name, packageManager))
|
||||
);
|
||||
delete tempBuildPack.name;
|
||||
delete tempBuildPack.fancyName;
|
||||
delete tempBuildPack.color;
|
||||
|
||||
@@ -29,15 +29,19 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { buildPacks, scanningTemplates } from '$lib/components/templates';
|
||||
import { buildPacks, findBuildPack, scanningTemplates } from '$lib/components/templates';
|
||||
import BuildPack from './_BuildPack.svelte';
|
||||
import { page, session } from '$app/stores';
|
||||
import { get } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
let scanning = true;
|
||||
let foundConfig = null;
|
||||
let packageManager = 'npm';
|
||||
|
||||
export let apiUrl;
|
||||
export let projectId;
|
||||
@@ -49,10 +53,11 @@
|
||||
function checkPackageJSONContents({ key, json }) {
|
||||
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
|
||||
}
|
||||
function checkTemplates({ json }) {
|
||||
function checkTemplates({ json, packageManager }) {
|
||||
for (const [key, value] of Object.entries(scanningTemplates)) {
|
||||
if (checkPackageJSONContents({ key, json })) {
|
||||
return buildPacks.find((bp) => bp.name === value.buildPack);
|
||||
foundConfig = findBuildPack(value.buildPack, packageManager);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +70,10 @@
|
||||
const packageJson = files.find(
|
||||
(file) => file.name === 'package.json' && file.type === 'blob'
|
||||
);
|
||||
const yarnLock = files.find((file) => file.name === 'yarn.lock' && file.type === 'blob');
|
||||
const pnpmLock = files.find(
|
||||
(file) => file.name === 'pnpm-lock.yaml' && file.type === 'blob'
|
||||
);
|
||||
const dockerfile = files.find((file) => file.name === 'Dockerfile' && file.type === 'blob');
|
||||
const cargoToml = files.find((file) => file.name === 'Cargo.toml' && file.type === 'blob');
|
||||
const requirementsTxt = files.find(
|
||||
@@ -72,8 +81,12 @@
|
||||
);
|
||||
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'blob');
|
||||
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'blob');
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
|
||||
if (dockerfile) {
|
||||
foundConfig.buildPack = 'docker';
|
||||
foundConfig = findBuildPack('docker', packageManager);
|
||||
} else if (packageJson) {
|
||||
const path = packageJson.path;
|
||||
const data = await get(
|
||||
@@ -83,15 +96,17 @@
|
||||
}
|
||||
);
|
||||
const json = JSON.parse(data) || {};
|
||||
foundConfig = checkTemplates({ json });
|
||||
checkTemplates({ json, packageManager });
|
||||
} else if (cargoToml) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'rust');
|
||||
foundConfig = findBuildPack('rust');
|
||||
} else if (requirementsTxt) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'python');
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'static');
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'php');
|
||||
foundConfig = findBuildPack('php');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
}
|
||||
} else if (type === 'github') {
|
||||
const files = await get(`${apiUrl}/repos/${repository}/contents?ref=${branch}`, {
|
||||
@@ -101,6 +116,10 @@
|
||||
const packageJson = files.find(
|
||||
(file) => file.name === 'package.json' && file.type === 'file'
|
||||
);
|
||||
const yarnLock = files.find((file) => file.name === 'yarn.lock' && file.type === 'file');
|
||||
const pnpmLock = files.find(
|
||||
(file) => file.name === 'pnpm-lock.yaml' && file.type === 'file'
|
||||
);
|
||||
const dockerfile = files.find((file) => file.name === 'Dockerfile' && file.type === 'file');
|
||||
const cargoToml = files.find((file) => file.name === 'Cargo.toml' && file.type === 'file');
|
||||
const requirementsTxt = files.find(
|
||||
@@ -108,26 +127,33 @@
|
||||
);
|
||||
const indexHtml = files.find((file) => file.name === 'index.html' && file.type === 'file');
|
||||
const indexPHP = files.find((file) => file.name === 'index.php' && file.type === 'file');
|
||||
|
||||
if (yarnLock) packageManager = 'yarn';
|
||||
if (pnpmLock) packageManager = 'pnpm';
|
||||
|
||||
if (dockerfile) {
|
||||
foundConfig.buildPack = 'docker';
|
||||
foundConfig = findBuildPack('docker', packageManager);
|
||||
} else if (packageJson) {
|
||||
const data = await get(`${packageJson.git_url}`, {
|
||||
Authorization: `Bearer ${$gitTokens.githubToken}`,
|
||||
Accept: 'application/vnd.github.v2.raw'
|
||||
});
|
||||
const json = JSON.parse(data) || {};
|
||||
foundConfig = checkTemplates({ json });
|
||||
checkTemplates({ json, packageManager });
|
||||
} else if (cargoToml) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'rust');
|
||||
foundConfig = findBuildPack('rust');
|
||||
} else if (requirementsTxt) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'python');
|
||||
foundConfig = findBuildPack('python');
|
||||
} else if (indexHtml) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'static');
|
||||
foundConfig = findBuildPack('static', packageManager);
|
||||
} else if (indexPHP) {
|
||||
foundConfig = buildPacks.find((bp) => bp.name === 'php');
|
||||
foundConfig = findBuildPack('php');
|
||||
} else {
|
||||
foundConfig = findBuildPack('node', packageManager);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
scanning = true;
|
||||
if (
|
||||
error.error === 'invalid_token' ||
|
||||
error.error_description ===
|
||||
@@ -155,9 +181,14 @@
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
if (error.message === 'Bad credentials') {
|
||||
const { token } = await get(`/applications/${id}/configuration/githubToken.json`);
|
||||
$gitTokens.githubToken = token;
|
||||
browser && window.location.reload();
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
if (!foundConfig) foundConfig = buildPacks.find((bp) => bp.name === 'node');
|
||||
if (!foundConfig) foundConfig = findBuildPack('node', packageManager);
|
||||
scanning = false;
|
||||
}
|
||||
}
|
||||
@@ -175,10 +206,16 @@
|
||||
<div class="text-xl tracking-tight">Scanning repository to suggest a build pack for you...</div>
|
||||
</div>
|
||||
{:else}
|
||||
{#if packageManager === 'yarn' || packageManager === 'pnpm'}
|
||||
<div class="flex justify-center p-6">
|
||||
Found lock file for <span class="font-bold text-orange-500 pl-1">{packageManager}</span>.
|
||||
Using it for predefined commands commands.
|
||||
</div>
|
||||
{/if}
|
||||
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
|
||||
{#each buildPacks as buildPack}
|
||||
<div class="p-2">
|
||||
<BuildPack {buildPack} {scanning} bind:foundConfig />
|
||||
<BuildPack {buildPack} {scanning} {packageManager} bind:foundConfig />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import type Prisma from '@prisma/client';
|
||||
|
||||
import { page } from '$app/stores';
|
||||
import { enhance, errorNotification } from '$lib/form';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { goto } from '$app/navigation';
|
||||
import { post } from '$lib/api';
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export const post: RequestHandler = async (event) => {
|
||||
.digest('hex');
|
||||
await db.prisma.application.update({ where: { id }, data: { configHash } });
|
||||
}
|
||||
await db.prisma.application.update({ where: { id }, data: { updatedAt: new Date() } });
|
||||
await buildQueue.add(buildId, { build_id: buildId, type: 'manual', ...applicationFound });
|
||||
return {
|
||||
status: 200,
|
||||
|
||||
@@ -45,15 +45,22 @@
|
||||
import { notNodeDeployments, staticDeployments } from '$lib/components/common';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { post } from '$lib/api';
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
const { id } = $page.params;
|
||||
|
||||
let domainEl: HTMLInputElement;
|
||||
|
||||
let loading = false;
|
||||
let forceSave = false;
|
||||
let debug = application.settings.debug;
|
||||
let previews = application.settings.previews;
|
||||
let dualCerts = application.settings.dualCerts;
|
||||
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
domainEl.focus();
|
||||
});
|
||||
@@ -78,10 +85,13 @@
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
try {
|
||||
await post(`/applications/${id}/check.json`, { fqdn: application.fqdn });
|
||||
await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave });
|
||||
await post(`/applications/${id}.json`, { ...application });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
if (error.startsWith('DNS not set')) {
|
||||
forceSave = true;
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
@@ -167,112 +177,110 @@
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-green-600={!loading}
|
||||
class:bg-orange-600={forceSave}
|
||||
class:hover:bg-green-500={!loading}
|
||||
disabled={loading}>{loading ? 'Saving...' : 'Save'}</button
|
||||
class:hover:bg-orange-400={forceSave}
|
||||
disabled={loading}
|
||||
>{loading ? 'Saving...' : forceSave ? 'Are you sure to continue?' : 'Save'}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="mt-2 grid grid-cols-3 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2 ">
|
||||
<div class="mt-2 grid grid-cols-2 items-center">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="name"
|
||||
id="name"
|
||||
bind:value={application.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="gitSource" class="text-base font-bold text-stone-100">Git Source</label>
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/source?from=/applications/${id}`
|
||||
: ''}
|
||||
class="no-underline"
|
||||
><input
|
||||
value={application.gitSource.name}
|
||||
id="gitSource"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="repository" class="text-base font-bold text-stone-100">Git Repository</label>
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`
|
||||
: ''}
|
||||
class="no-underline"
|
||||
><input
|
||||
value="{application.repository}/{application.branch}"
|
||||
id="repository"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="buildPack" class="text-base font-bold text-stone-100">Build Pack</label>
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/buildpack?from=/applications/${id}`
|
||||
: ''}
|
||||
class="no-underline "
|
||||
>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="name"
|
||||
id="name"
|
||||
bind:value={application.name}
|
||||
required
|
||||
value={application.buildPack}
|
||||
id="buildPack"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
||||
<div class="no-underline">
|
||||
<input
|
||||
value={application.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
class="bg-transparent "
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="gitSource">Git Source</label>
|
||||
<div class="col-span-2">
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/source?from=/applications/${id}`
|
||||
: ''}
|
||||
class="no-underline"
|
||||
><input
|
||||
value={application.gitSource.name}
|
||||
id="gitSource"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="repository">Git Repository</label>
|
||||
<div class="col-span-2">
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`
|
||||
: ''}
|
||||
class="no-underline"
|
||||
><input
|
||||
value="{application.repository}/{application.branch}"
|
||||
id="repository"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="buildPack">Build Pack</label>
|
||||
<div class="col-span-2">
|
||||
<a
|
||||
href={$session.isAdmin
|
||||
? `/applications/${id}/configuration/buildpack?from=/applications/${id}`
|
||||
: ''}
|
||||
class="no-underline "
|
||||
>
|
||||
<input
|
||||
value={application.buildPack}
|
||||
id="buildPack"
|
||||
disabled
|
||||
class="cursor-pointer hover:bg-coolgray-500"
|
||||
/></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center pb-8">
|
||||
<label for="destination">Destination</label>
|
||||
<div class="col-span-2">
|
||||
<div class="no-underline">
|
||||
<input
|
||||
value={application.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
class="bg-transparent "
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-1 py-5 font-bold">
|
||||
<div class="title">Application</div>
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-cols-3">
|
||||
<label for="fqdn" class="relative pt-2">Domain (FQDN)</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
bind:value={application.fqdn}
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="eg: https://coollabs.io"
|
||||
required
|
||||
/>
|
||||
<div class="grid grid-cols-2">
|
||||
<div class="flex-col">
|
||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100">Domain (FQDN)</label>
|
||||
{#if browser && window.location.hostname === 'demo.coolify.io'}
|
||||
<Explainer
|
||||
text="<span class='text-white font-bold'>You can use the predefined random domain name or enter your own domain name.</span>"
|
||||
/>
|
||||
{/if}
|
||||
<Explainer
|
||||
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."
|
||||
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.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
bind:value={application.fqdn}
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
placeholder="eg: https://coollabs.io"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<Setting
|
||||
@@ -286,89 +294,88 @@
|
||||
/>
|
||||
</div>
|
||||
{#if !staticDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="port">Port</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="port"
|
||||
id="port"
|
||||
bind:value={application.port}
|
||||
placeholder="default: 3000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="installCommand">Install Command</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="installCommand"
|
||||
id="installCommand"
|
||||
bind:value={application.installCommand}
|
||||
placeholder="default: yarn install"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="buildCommand">Build Command</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="buildCommand"
|
||||
id="buildCommand"
|
||||
bind:value={application.buildCommand}
|
||||
placeholder="default: yarn build"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="startCommand" class="">Start Command</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="startCommand"
|
||||
id="startCommand"
|
||||
bind:value={application.startCommand}
|
||||
placeholder="default: yarn start"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-3">
|
||||
<label for="baseDirectory" class="pt-2">Base Directory</label>
|
||||
<div class="col-span-2">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="port" class="text-base font-bold text-stone-100">Port</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="baseDirectory"
|
||||
id="baseDirectory"
|
||||
bind:value={application.baseDirectory}
|
||||
placeholder="default: /"
|
||||
/>
|
||||
<Explainer
|
||||
text="Directory to use as the base of all commands. <br> Could be useful with monorepos."
|
||||
name="port"
|
||||
id="port"
|
||||
bind:value={application.port}
|
||||
placeholder="default: 3000"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="installCommand" class="text-base font-bold text-stone-100"
|
||||
>Install Command</label
|
||||
>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="installCommand"
|
||||
id="installCommand"
|
||||
bind:value={application.installCommand}
|
||||
placeholder="default: yarn install"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="buildCommand" class="text-base font-bold text-stone-100">Build Command</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="buildCommand"
|
||||
id="buildCommand"
|
||||
bind:value={application.buildCommand}
|
||||
placeholder="default: yarn build"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="startCommand" class="text-base font-bold text-stone-100">Start Command</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="startCommand"
|
||||
id="startCommand"
|
||||
bind:value={application.startCommand}
|
||||
placeholder="default: yarn start"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div class="flex-col">
|
||||
<label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100"
|
||||
>Base Directory</label
|
||||
>
|
||||
<Explainer
|
||||
text="Directory to use as the base for all commands.<br>Could be useful with <span class='text-green-500 font-bold'>monorepos</span>."
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="baseDirectory"
|
||||
id="baseDirectory"
|
||||
bind:value={application.baseDirectory}
|
||||
placeholder="default: /"
|
||||
/>
|
||||
</div>
|
||||
{#if !notNodeDeployments.includes(application.buildPack)}
|
||||
<div class="grid grid-cols-3">
|
||||
<label for="publishDirectory" class="pt-2">Publish Directory</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="publishDirectory"
|
||||
id="publishDirectory"
|
||||
bind:value={application.publishDirectory}
|
||||
placeholder=" default: /"
|
||||
/>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div class="flex-col">
|
||||
<label for="publishDirectory" class="pt-2 text-base font-bold text-stone-100"
|
||||
>Publish Directory</label
|
||||
>
|
||||
<Explainer
|
||||
text="Directory containing all the assets for deployment. <br> For example: <span class='text-green-600 font-bold'>dist</span>,<span class='text-green-600 font-bold'>_site</span> or <span class='text-green-600 font-bold'>public</span>."
|
||||
text="Directory containing all the assets for deployment. <br> For example: <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> or <span class='text-green-500 font-bold'>public</span>."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="publishDirectory"
|
||||
id="publishDirectory"
|
||||
bind:value={application.publishDirectory}
|
||||
placeholder=" default: /"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -400,7 +407,7 @@
|
||||
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)"
|
||||
description="Enable debug logs during build phase.<br><span class='text-red-500 font-bold'>Sensitive information</span> could be visible and saved in logs."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
build.took = data.builds[0].took;
|
||||
build.since = data.builds[0].since;
|
||||
}
|
||||
window.location.reload();
|
||||
return build;
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -43,53 +43,41 @@
|
||||
</div>
|
||||
</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 applicationSecrets as secret}
|
||||
{#key secret.id}
|
||||
<tr class="h-20 transition duration-100 hover:bg-coolgray-400">
|
||||
<Secret
|
||||
PRMRSecret={PRMRSecrets.find((s) => s.name === secret.name)}
|
||||
isPRMRSecret
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
isBuildSecret={secret.isBuildSecret}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
</tr>
|
||||
{/key}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{#if applicationSecrets.length !== 0}
|
||||
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
|
||||
<table class="mx-auto border-separate text-left">
|
||||
<thead>
|
||||
<tr class="h-12">
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Value</th>
|
||||
<th scope="col" class="w-64 text-center">Need during buildtime?</th>
|
||||
<th scope="col" class="w-96 text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each applicationSecrets as secret}
|
||||
{#key secret.id}
|
||||
<tr>
|
||||
<Secret
|
||||
PRMRSecret={PRMRSecrets.find((s) => s.name === secret.name)}
|
||||
isPRMRSecret
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
isBuildSecret={secret.isBuildSecret}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
</tr>
|
||||
{/key}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-center py-4 text-center">
|
||||
<Explainer
|
||||
customClass="w-full"
|
||||
text={applicationSecrets.length === 0
|
||||
? "<span class='font-bold text-white text-xl'>Please add secrets to the application first.</span> <br><br>These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||
? "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||
: "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -59,19 +59,19 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||
<td>
|
||||
<input
|
||||
id={isNewSecret ? 'secretName' : 'secretNameNew'}
|
||||
bind:value={name}
|
||||
required
|
||||
placeholder="EXAMPLE_VARIABLE"
|
||||
class="-mx-2 w-64 border-2 border-transparent"
|
||||
class=" border border-dashed border-coolgray-300"
|
||||
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">
|
||||
<td>
|
||||
<CopyPasswordField
|
||||
id={isNewSecret ? 'secretValue' : 'secretValueNew'}
|
||||
name={isNewSecret ? 'secretValue' : 'secretValueNew'}
|
||||
@@ -81,7 +81,7 @@
|
||||
placeholder="J$#@UIO%HO#$U%H"
|
||||
/>
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-6 py-2 text-center text-sm font-medium text-white">
|
||||
<td class="text-center">
|
||||
<div
|
||||
type="button"
|
||||
on:click={setSecretValue}
|
||||
@@ -130,21 +130,19 @@
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="whitespace-nowrap px-6 py-2 text-sm font-medium text-white">
|
||||
<td>
|
||||
{#if isNewSecret}
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveSecret(true)}>Add</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex-col space-y-2">
|
||||
<div class="flex flex-row justify-center space-x-2">
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="w-24 bg-green-600 hover:bg-green-500" on:click={() => saveSecret(false)}
|
||||
>Set</button
|
||||
>
|
||||
<button class="" on:click={() => saveSecret(false)}>Set</button>
|
||||
</div>
|
||||
{#if !isPRMRSecret}
|
||||
<div class="flex justify-center items-end">
|
||||
<button class="w-24 bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
||||
<button class="bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -41,33 +41,19 @@
|
||||
</div>
|
||||
</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"
|
||||
/>
|
||||
<table class="mx-auto border-separate text-left">
|
||||
<thead>
|
||||
<tr class="h-12">
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Value</th>
|
||||
<th scope="col" class="w-64 text-center">Need during buildtime?</th>
|
||||
<th scope="col" class="w-96 text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
<tbody>
|
||||
{#each secrets as secret}
|
||||
{#key secret.id}
|
||||
<tr class="h-20 transition duration-100 hover:bg-coolgray-400">
|
||||
<tr>
|
||||
<Secret
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import { asyncExecShell, getEngine, getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { removeProxyConfiguration } from '$lib/haproxy';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -17,10 +16,12 @@ export const post: RequestHandler = async (event) => {
|
||||
teamId
|
||||
});
|
||||
if (destinationDockerId) {
|
||||
const docker = dockerInstance({ destinationDocker });
|
||||
await docker.engine.getContainer(id).stop();
|
||||
const { engine } = destinationDocker;
|
||||
const found = await checkContainer(engine, id);
|
||||
if (found) {
|
||||
await removeDestinationDocker({ id, engine });
|
||||
}
|
||||
}
|
||||
await removeProxyConfiguration(fqdn);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
import Nextjs from '$lib/components/svg/applications/Nextjs.svelte';
|
||||
import Gatsby from '$lib/components/svg/applications/Gatsby.svelte';
|
||||
import Docker from '$lib/components/svg/applications/Docker.svelte';
|
||||
import Astro from '$lib/components/svg/applications/Astro.svelte';
|
||||
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
|
||||
|
||||
const buildPack = application?.buildPack?.toLowerCase();
|
||||
</script>
|
||||
@@ -45,6 +47,10 @@
|
||||
<Gatsby />
|
||||
{:else if buildPack === 'docker'}
|
||||
<Docker />
|
||||
{:else if buildPack === 'astro'}
|
||||
<Astro />
|
||||
{:else if buildPack === 'eleventy'}
|
||||
<Eleventy />
|
||||
{/if}
|
||||
|
||||
<div class="truncate text-center text-xl font-bold">{application.name}</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<label for="defaultDatabase" class="text-base font-bold text-stone-100">Default Database</label>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<label for="dbUser" class="text-base font-bold text-stone-100">User</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -31,7 +31,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<label for="dbUserPassword" class="text-base font-bold text-stone-100">Password</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -43,7 +43,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<label for="rootUser" class="text-base font-bold text-stone-100">Root User</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -54,7 +54,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<label for="rootUserPassword" class="text-base font-bold text-stone-100">Root's Password</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
|
||||
@@ -56,9 +56,11 @@
|
||||
appendOnly = !appendOnly;
|
||||
}
|
||||
try {
|
||||
await post(`/databases/${id}/settings.json`, { isPublic, appendOnly });
|
||||
const { publicPort } = await post(`/databases/${id}/settings.json`, { isPublic, appendOnly });
|
||||
if (isPublic) {
|
||||
database.publicPort = publicPort;
|
||||
}
|
||||
databaseUrl = generateUrl();
|
||||
return;
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
@@ -89,7 +91,7 @@
|
||||
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="name">Name</label>
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
name="name"
|
||||
@@ -99,7 +101,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="destination">Destination</label>
|
||||
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
||||
{#if database.destinationDockerId}
|
||||
<div class="no-underline">
|
||||
<input
|
||||
@@ -114,14 +116,14 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="version">Version</label>
|
||||
<label for="version" class="text-base font-bold text-stone-100">Version</label>
|
||||
<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-flow-row gap-2 px-10 pt-2">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="host">Host</label>
|
||||
<label for="host" class="text-base font-bold text-stone-100">Host</label>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={false}
|
||||
@@ -133,9 +135,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="publicPort">Port</label>
|
||||
<label for="publicPort" class="text-base font-bold text-stone-100">Port</label>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
placeholder="Generated automatically after set to public"
|
||||
id="publicPort"
|
||||
readonly
|
||||
disabled
|
||||
@@ -157,7 +159,7 @@
|
||||
<CouchDb bind:database />
|
||||
{/if}
|
||||
<div class="grid grid-cols-2 items-center px-10 pb-8">
|
||||
<label for="url">Connection String</label>
|
||||
<label for="url" class="text-base font-bold text-stone-100">Connection String</label>
|
||||
<CopyPasswordField
|
||||
textarea={true}
|
||||
placeholder="Generated automatically after start"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<label for="rootUser" class="text-base font-bold text-stone-100">Root User</label>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
id="rootUser"
|
||||
@@ -19,7 +19,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<label for="rootUserPassword" class="text-base font-bold text-stone-100">Root's Password</label>
|
||||
<CopyPasswordField
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField={true}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<label for="defaultDatabase" class="text-base font-bold text-stone-100">Default Database</label>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<label for="dbUser" class="text-base font-bold text-stone-100">User</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -31,7 +31,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<label for="dbUserPassword" class="text-base font-bold text-stone-100">Password</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -43,7 +43,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser">Root User</label>
|
||||
<label for="rootUser" class="text-base font-bold text-stone-100">Root User</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -54,7 +54,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUserPassword">Root's Password</label>
|
||||
<label for="rootUserPassword" class="text-base font-bold text-stone-100">Root's Password</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="defaultDatabase">Default Database</label>
|
||||
<label for="defaultDatabase" class="text-base font-bold text-stone-100">Default Database</label>
|
||||
<CopyPasswordField
|
||||
required
|
||||
readonly={database.defaultDatabase}
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">User</label>
|
||||
<label for="dbUser" class="text-base font-bold text-stone-100">User</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
@@ -31,7 +31,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<label for="dbUserPassword" class="text-base font-bold text-stone-100">Password</label>
|
||||
<CopyPasswordField
|
||||
readonly
|
||||
disabled
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class="space-y-2 px-10">
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUserPassword">Password</label>
|
||||
<label for="dbUserPassword" class="text-base font-bold text-stone-100">Password</label>
|
||||
<CopyPasswordField
|
||||
disabled
|
||||
readonly
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
title="Stop database"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-purple-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Stop database'
|
||||
: 'You do not have permission to stop the database.'}
|
||||
@@ -146,7 +146,7 @@
|
||||
title="Start database"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-purple-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Start database'
|
||||
: 'You do not have permission to start the database.'}
|
||||
|
||||
@@ -3,30 +3,39 @@ import * as db from '$lib/database';
|
||||
import { generateDatabaseConfiguration, ErrorHandler } from '$lib/database';
|
||||
import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body, teamId } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
const data = await db.prisma.setting.findFirst();
|
||||
const { minPort, maxPort } = data;
|
||||
|
||||
const { isPublic, appendOnly = true } = await event.request.json();
|
||||
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
|
||||
|
||||
try {
|
||||
await db.setDatabase({ id, isPublic, appendOnly });
|
||||
const database = await db.getDatabase({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, publicPort } = database;
|
||||
const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
|
||||
const { privatePort } = generateDatabaseConfiguration(database);
|
||||
|
||||
if (destinationDockerId) {
|
||||
if (isPublic) {
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort } });
|
||||
await startTcpProxy(destinationDocker, id, publicPort, privatePort);
|
||||
} else {
|
||||
await stopTcpHttpProxy(destinationDocker, publicPort);
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 201
|
||||
status: 201,
|
||||
body: {
|
||||
publicPort
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -15,6 +15,7 @@ export const post: RequestHandler = async (event) => {
|
||||
const everStarted = await stopDatabase(database);
|
||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
||||
await db.setDatabase({ id, isPublic: false });
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||
|
||||
return {
|
||||
status: 200
|
||||
|
||||
@@ -61,12 +61,12 @@
|
||||
<div class="w-full text-center font-bold">Loading...</div>
|
||||
{:else if app.foundByDomain}
|
||||
<div class="w-full bg-coolgray-200 text-xs">
|
||||
<span class="text-red-500">Domain</span> already configured for
|
||||
<span class="text-red-500">Domain</span> already used for
|
||||
<span class="text-red-500">{app.foundName}</span>
|
||||
</div>
|
||||
{:else if app.foundByRepository}
|
||||
<div class="w-full bg-coolgray-200 text-xs">
|
||||
<span class="text-red-500">Repository</span> already configured for
|
||||
<span class="text-red-500">Repository</span> already used for
|
||||
<span class="text-red-500">{app.foundName}</span>
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
export let state;
|
||||
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { page } from '$app/stores';
|
||||
import { page, session } from '$app/stores';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { post } from '$lib/api';
|
||||
@@ -117,15 +117,17 @@
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
} finally {
|
||||
restarting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center px-6 pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex space-x-1 pb-5">
|
||||
<div class="title font-bold">Configuration</div>
|
||||
{#if $session.isAdmin}
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-sky-600 hover:bg-sky-500"
|
||||
@@ -140,62 +142,62 @@
|
||||
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}
|
||||
{/if}
|
||||
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||
>Scan for applications</button
|
||||
> -->
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2">
|
||||
<input name="name" placeholder="name" bind:value={destination.name} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10 ">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
name="name"
|
||||
placeholder="name"
|
||||
disabled={!$session.isAdmin}
|
||||
readonly={!$session.isAdmin}
|
||||
bind:value={destination.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="engine">Engine</label>
|
||||
<div class="col-span-2">
|
||||
<CopyPasswordField
|
||||
id="engine"
|
||||
readonly
|
||||
disabled
|
||||
name="engine"
|
||||
placeholder="eg: /var/run/docker.sock"
|
||||
value={destination.engine}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="flex items-center">
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="engine" class="text-base font-bold text-stone-100">Engine</label>
|
||||
<CopyPasswordField
|
||||
id="engine"
|
||||
readonly
|
||||
disabled
|
||||
name="engine"
|
||||
placeholder="eg: /var/run/docker.sock"
|
||||
value={destination.engine}
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="flex items-center">
|
||||
<label for="remoteEngine">Remote Engine?</label>
|
||||
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
||||
</div> -->
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="network">Network</label>
|
||||
<div class="col-span-2">
|
||||
<CopyPasswordField
|
||||
id="network"
|
||||
readonly
|
||||
disabled
|
||||
name="network"
|
||||
placeholder="default: coolify"
|
||||
value={destination.network}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||
<CopyPasswordField
|
||||
id="network"
|
||||
readonly
|
||||
disabled
|
||||
name="network"
|
||||
placeholder="default: coolify"
|
||||
value={destination.network}
|
||||
/>
|
||||
</div>
|
||||
<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 class="flex justify-center">
|
||||
{#if payload.isCoolifyProxyUsed}
|
||||
{#if state}
|
||||
|
||||
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
@@ -0,0 +1,225 @@
|
||||
<script lang="ts">
|
||||
export let destination;
|
||||
export let settings;
|
||||
export let state;
|
||||
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { page, session } from '$app/stores';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { post } from '$lib/api';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { generateRemoteEngine } from '$lib/components/common';
|
||||
const { id } = $page.params;
|
||||
let cannotDisable = settings.fqdn && destination.engine === '/var/run/docker.sock';
|
||||
// let scannedApps = [];
|
||||
let loading = false;
|
||||
let restarting = false;
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
try {
|
||||
return await post(`/destinations/${id}.json`, { ...destination });
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
// async function scanApps() {
|
||||
// scannedApps = [];
|
||||
// const data = await fetch(`/destinations/${id}/scan.json`);
|
||||
// const { containers } = await data.json();
|
||||
// scannedApps = containers;
|
||||
// }
|
||||
onMount(async () => {
|
||||
if (state === false && destination.isCoolifyProxyUsed === true) {
|
||||
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||
try {
|
||||
await post(`/destinations/${id}/settings.json`, {
|
||||
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||
engine: destination.engine
|
||||
});
|
||||
await stopProxy();
|
||||
} 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() {
|
||||
if (!cannotDisable) {
|
||||
const isProxyActivated = destination.isCoolifyProxyUsed;
|
||||
if (isProxyActivated) {
|
||||
const sure = confirm(
|
||||
`Are you sure you want to ${
|
||||
destination.isCoolifyProxyUsed ? 'disable' : 'enable'
|
||||
} Coolify proxy? It will remove the proxy for all configured networks and all deployments on '${
|
||||
destination.engine
|
||||
}'! Nothing will be reachable if you do it!`
|
||||
);
|
||||
if (!sure) return;
|
||||
}
|
||||
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||
try {
|
||||
await post(`/destinations/${id}/settings.json`, {
|
||||
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||
engine: destination.engine
|
||||
});
|
||||
if (isProxyActivated) {
|
||||
await stopProxy();
|
||||
} else {
|
||||
await startProxy();
|
||||
}
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function stopProxy() {
|
||||
try {
|
||||
const engine = generateRemoteEngine(destination);
|
||||
await post(`/destinations/${id}/stop.json`, { engine });
|
||||
return toast.push('Coolify Proxy stopped!');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function startProxy() {
|
||||
try {
|
||||
const engine = generateRemoteEngine(destination);
|
||||
await post(`/destinations/${id}/start.json`, { engine });
|
||||
return toast.push('Coolify Proxy started!');
|
||||
} catch ({ error }) {
|
||||
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>
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex space-x-1 pb-5">
|
||||
<div class="title font-bold">Configuration</div>
|
||||
{#if $session.isAdmin}
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-sky-600 hover:bg-sky-500"
|
||||
class:bg-sky-600={!loading}
|
||||
class:hover:bg-sky-500={!loading}
|
||||
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
|
||||
>
|
||||
{/if}
|
||||
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||
>Scan for applications</button
|
||||
> -->
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10 ">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
name="name"
|
||||
placeholder="name"
|
||||
disabled={!$session.isAdmin}
|
||||
readonly={!$session.isAdmin}
|
||||
bind:value={destination.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="engine" class="text-base font-bold text-stone-100">Engine</label>
|
||||
<CopyPasswordField
|
||||
id="engine"
|
||||
readonly
|
||||
disabled
|
||||
name="engine"
|
||||
placeholder="eg: /var/run/docker.sock"
|
||||
value={destination.engine}
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="flex items-center">
|
||||
<label for="remoteEngine">Remote Engine?</label>
|
||||
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
||||
</div> -->
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||
<CopyPasswordField
|
||||
id="network"
|
||||
readonly
|
||||
disabled
|
||||
name="network"
|
||||
placeholder="default: coolify"
|
||||
value={destination.network}
|
||||
/>
|
||||
</div>
|
||||
<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 class="flex justify-center">
|
||||
{#if payload.isCoolifyProxyUsed}
|
||||
{#if state}
|
||||
<button on:click={stopProxy}>Stop proxy</button>
|
||||
{:else}
|
||||
<button on:click={startProxy}>Start proxy</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</div> -->
|
||||
|
||||
<!-- {#if scannedApps.length > 0}
|
||||
<div class="flex justify-center px-6 pb-10">
|
||||
<div class="flex space-x-2 h-8 items-center">
|
||||
<div class="font-bold text-xl text-white">Found applications</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-4xl mx-auto px-6">
|
||||
<div class="flex space-x-2 justify-center">
|
||||
{#each scannedApps as app}
|
||||
<FoundApp {app} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if} -->
|
||||
@@ -61,8 +61,8 @@
|
||||
class:hover:text-red-500={$session.isAdmin}
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Delete Git Source'
|
||||
: 'You do not have permission to delete a Git Source'}><DeleteIcon /></button
|
||||
? 'Delete Destination'
|
||||
: 'You do not have permission to delete this destination'}><DeleteIcon /></button
|
||||
>
|
||||
</nav>
|
||||
<slot />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { asyncExecShell, getEngine, getTeam, getUserDetails } from '$lib/common';
|
||||
import { asyncExecShell, getUserDetails } from '$lib/common';
|
||||
import { generateRemoteEngine } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
@@ -12,15 +13,26 @@ export const get: RequestHandler = async (event) => {
|
||||
try {
|
||||
const destination = await db.getDestination({ id, teamId });
|
||||
const settings = await db.listSettings();
|
||||
const state =
|
||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||
let payload = {
|
||||
destination,
|
||||
settings,
|
||||
state: false
|
||||
};
|
||||
if (destination.remoteEngine) {
|
||||
// const { stdout } = await asyncExecShell(
|
||||
// `ssh -p ${destination.port} ${destination.user}@${destination.ipAddress} "docker ps -a"`
|
||||
// );
|
||||
// console.log(stdout)
|
||||
// const engine = await generateRemoteEngine(destination);
|
||||
// // await saveSshKey(destination);
|
||||
// payload.state = await checkContainer(engine, 'coolify-haproxy');
|
||||
} else {
|
||||
payload.state =
|
||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
destination,
|
||||
settings,
|
||||
state
|
||||
}
|
||||
body: { ...payload }
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
import type Prisma from '@prisma/client';
|
||||
import LocalDocker from './_LocalDocker.svelte';
|
||||
import RemoteDocker from './_RemoteDocker.svelte';
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 text-2xl font-bold">
|
||||
@@ -43,4 +44,10 @@
|
||||
<span class="pr-2">{destination.name}</span>
|
||||
</div>
|
||||
|
||||
<LocalDocker bind:destination {settings} {state} />
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
{#if destination.remoteEngine}
|
||||
<RemoteDocker bind:destination {settings} {state} />
|
||||
{:else}
|
||||
<LocalDocker bind:destination {settings} {state} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,19 @@
|
||||
import { getDomain, getUserDetails } from '$lib/common';
|
||||
import { 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 { 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();
|
||||
const { engine } = 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
|
||||
};
|
||||
|
||||
@@ -4,10 +4,10 @@ import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { email, password } = await event.request.json();
|
||||
const { email, password, isLogin } = await event.request.json();
|
||||
|
||||
try {
|
||||
const { body } = await db.login({ email, password });
|
||||
const { body } = await db.login({ email, password, isLogin });
|
||||
event.locals.session.data = body;
|
||||
return {
|
||||
status: 200
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
try {
|
||||
const { teamId } = await post(`/login.json`, { email, password });
|
||||
const { teamId } = await post(`/login.json`, {
|
||||
email: email.toLowerCase(),
|
||||
password,
|
||||
isLogin: true
|
||||
});
|
||||
if (teamId === '0') {
|
||||
window.location.replace('/settings');
|
||||
} else {
|
||||
@@ -58,7 +62,7 @@
|
||||
required
|
||||
/>
|
||||
|
||||
<div class="flex space-x-2 h-8 items-center justify-center pt-14">
|
||||
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
@@ -67,9 +71,27 @@
|
||||
class:text-stone-600={loading}
|
||||
class:bg-coollabs={!loading}>{loading ? 'Authenticating...' : 'Login'}</button
|
||||
>
|
||||
<button on:click|preventDefault={() => goto('/reset')}>Reset password</button>
|
||||
|
||||
<button
|
||||
on:click|preventDefault={() => goto('/register')}
|
||||
class="bg-transparent hover:bg-coolgray-300 text-white ">Register</button
|
||||
>
|
||||
<button
|
||||
class="bg-transparent hover:bg-coolgray-300"
|
||||
on:click|preventDefault={() => goto('/reset')}>Reset password</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{#if browser && window.location.host === 'demo.coolify.io'}
|
||||
<div class="pt-5 font-bold">
|
||||
Registration is <span class="text-pink-500">open</span>, just fill in an email (does not
|
||||
need to be live email address for the demo instance) and a password.
|
||||
</div>
|
||||
<div class="pt-5 font-bold">
|
||||
All users gets an <span class="text-pink-500">own namespace</span>, so you won't be able to
|
||||
access other users data.
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let payload;
|
||||
|
||||
import { post } from '$lib/api';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { enhance, errorNotification } from '$lib/form';
|
||||
|
||||
let loading = false;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post('/new/destination/check.json', { network: payload.network });
|
||||
const { id } = await post('/new/destination/docker.json', {
|
||||
...payload
|
||||
});
|
||||
return await goto(`/destinations/${id}`);
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center px-6 pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-sky-600={!loading}
|
||||
class:hover:bg-sky-500={!loading}
|
||||
disabled={loading}
|
||||
>{loading
|
||||
? payload.isCoolifyProxyUsed
|
||||
? 'Saving and configuring proxy...'
|
||||
: 'Saving...'
|
||||
: 'Save'}</button
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2">
|
||||
<input required name="name" placeholder="name" bind:value={payload.name} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="engine">Engine</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
required
|
||||
name="engine"
|
||||
placeholder="eg: /var/run/docker.sock"
|
||||
bind:value={payload.engine}
|
||||
/>
|
||||
<!-- <Explainer text="You can use remote Docker Engine with over SSH." /> -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="flex items-center">
|
||||
<label for="remoteEngine">Remote Docker Engine?</label>
|
||||
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
||||
</div>
|
||||
{#if payload.remoteEngine}
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="user">User</label>
|
||||
<div class="col-span-2">
|
||||
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="port">Port</label>
|
||||
<div class="col-span-2">
|
||||
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
||||
</div>
|
||||
</div>
|
||||
{/if} -->
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="network">Network</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
required
|
||||
name="network"
|
||||
placeholder="default: coolify"
|
||||
bind:value={payload.network}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-start">
|
||||
<ul class="mt-2 divide-y divide-stone-800">
|
||||
<Setting
|
||||
bind:setting={payload.isCoolifyProxyUsed}
|
||||
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||
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 (recommended for Docker). Databases will have their own proxy."
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
68
src/routes/new/destination/_LocalDocker.svelte
Normal file
68
src/routes/new/destination/_LocalDocker.svelte
Normal file
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let payload;
|
||||
|
||||
import { post } from '$lib/api';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
|
||||
let loading = false;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post('/new/destination/check.json', { network: payload.network });
|
||||
const { id } = await post('/new/destination/docker.json', {
|
||||
...payload
|
||||
});
|
||||
return await goto(`/destinations/${id}`);
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center px-6 pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex items-center space-x-2 pb-5">
|
||||
<div class="title font-bold">Configuration</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-sky-600={!loading}
|
||||
class:hover:bg-sky-500={!loading}
|
||||
disabled={loading}
|
||||
>{loading
|
||||
? payload.isCoolifyProxyUsed
|
||||
? 'Saving and configuring proxy...'
|
||||
: 'Saving...'
|
||||
: 'Save'}</button
|
||||
>
|
||||
</div>
|
||||
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input required name="name" placeholder="name" bind:value={payload.name} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="engine" class="text-base font-bold text-stone-100">Engine</label>
|
||||
<input
|
||||
required
|
||||
name="engine"
|
||||
placeholder="eg: /var/run/docker.sock"
|
||||
bind:value={payload.engine}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={payload.isCoolifyProxyUsed}
|
||||
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||
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 (recommended for Docker).<br><br>Databases will have their own proxy."
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let payload;
|
||||
|
||||
import { post } from '$lib/api';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
|
||||
let loading = false;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const { id } = await post('/new/destination/docker.json', {
|
||||
...payload
|
||||
});
|
||||
return await goto(`/destinations/${id}`);
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center px-6 pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex items-center space-x-2 pb-5">
|
||||
<div class="title font-bold">Configuration</div>
|
||||
<button
|
||||
type="submit"
|
||||
class:bg-sky-600={!loading}
|
||||
class:hover:bg-sky-500={!loading}
|
||||
disabled={loading}
|
||||
>{loading
|
||||
? payload.isCoolifyProxyUsed
|
||||
? 'Saving and configuring proxy...'
|
||||
: 'Saving...'
|
||||
: 'Save'}</button
|
||||
>
|
||||
</div>
|
||||
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input required name="name" placeholder="name" bind:value={payload.name} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="ipAddress" class="text-base font-bold text-stone-100">IP Address</label>
|
||||
<input
|
||||
required
|
||||
name="ipAddress"
|
||||
placeholder="eg: 192.168..."
|
||||
bind:value={payload.ipAddress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="user" class="text-base font-bold text-stone-100">User</label>
|
||||
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="port" class="text-base font-bold text-stone-100">Port</label>
|
||||
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="sshPrivateKey" class="text-base font-bold text-stone-100">SSH Private Key</label>
|
||||
<textarea
|
||||
rows="10"
|
||||
class="resize-none"
|
||||
required
|
||||
name="sshPrivateKey"
|
||||
placeholder="eg: -----BEGIN...."
|
||||
bind:value={payload.sshPrivateKey}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
bind:setting={payload.isCoolifyProxyUsed}
|
||||
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||
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 (recommended for Docker).<br><br>Databases will have their own proxy."
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -8,10 +8,36 @@ export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { name, engine, network, isCoolifyProxyUsed } = await event.request.json();
|
||||
const {
|
||||
name,
|
||||
engine,
|
||||
network,
|
||||
isCoolifyProxyUsed,
|
||||
remoteEngine,
|
||||
ipAddress,
|
||||
user,
|
||||
port,
|
||||
sshPrivateKey
|
||||
} = await event.request.json();
|
||||
|
||||
try {
|
||||
const id = await db.newDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
||||
let id = null;
|
||||
if (remoteEngine) {
|
||||
id = await db.newRemoteDestination({
|
||||
name,
|
||||
teamId,
|
||||
engine,
|
||||
network,
|
||||
isCoolifyProxyUsed,
|
||||
remoteEngine,
|
||||
ipAddress,
|
||||
user,
|
||||
port,
|
||||
sshPrivateKey
|
||||
});
|
||||
} else {
|
||||
id = await db.newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
||||
}
|
||||
return { status: 200, body: { id } };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
<script>
|
||||
import Docker from './_Docker.svelte';
|
||||
|
||||
import LocalDocker from './_LocalDocker.svelte';
|
||||
import cuid from 'cuid';
|
||||
import RemoteDocker from './_RemoteDocker.svelte';
|
||||
let payload = {};
|
||||
let selected = 'docker';
|
||||
let selected = 'localDocker';
|
||||
|
||||
function setPredefined(type) {
|
||||
selected = type;
|
||||
switch (type) {
|
||||
case 'docker':
|
||||
case 'localDocker':
|
||||
payload = {
|
||||
name: 'Local Docker',
|
||||
engine: '/var/run/docker.sock',
|
||||
remoteEngine: false,
|
||||
user: 'root',
|
||||
port: 22,
|
||||
privateKey: null,
|
||||
network: 'coolify',
|
||||
network: cuid(),
|
||||
isCoolifyProxyUsed: true
|
||||
};
|
||||
break;
|
||||
case 'remoteDocker':
|
||||
payload = {
|
||||
name: 'Remote Docker',
|
||||
remoteEngine: true,
|
||||
ipAddress: null,
|
||||
user: 'root',
|
||||
port: 22,
|
||||
sshPrivateKey: null,
|
||||
network: cuid(),
|
||||
isCoolifyProxyUsed: true
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -32,12 +41,15 @@
|
||||
<div class="flex-col space-y-2 pb-10 text-center">
|
||||
<div class="text-xl font-bold text-white">Predefined destinations</div>
|
||||
<div class="flex justify-center space-x-2">
|
||||
<button class="w-32" on:click={() => setPredefined('docker')}>Docker</button>
|
||||
<button class="w-32" on:click={() => setPredefined('localDocker')}>Local Docker</button>
|
||||
<!-- <button class="w-32" on:click={() => setPredefined('remoteDocker')}>Remote Docker</button> -->
|
||||
<button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if selected === 'docker'}
|
||||
<Docker {payload} />
|
||||
{#if selected === 'localDocker'}
|
||||
<LocalDocker {payload} />
|
||||
{:else if selected === 'remoteDocker'}
|
||||
<RemoteDocker {payload} />
|
||||
{:else}
|
||||
<div class="text-center font-bold text-4xl py-10">Not implemented yet</div>
|
||||
{/if}
|
||||
|
||||
@@ -24,26 +24,24 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-center pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button type="submit" class="bg-orange-600 hover:bg-orange-500">Save</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="type">Type</label>
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<div class="flex justify-center pb-8">
|
||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||
<div class="flex h-8 items-center space-x-2">
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button type="submit" class="bg-orange-600 hover:bg-orange-500">Save</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="type" class="text-base font-bold text-stone-100">Type</label>
|
||||
|
||||
<div class="col-span-2">
|
||||
<select name="type" id="type" class="w-96" bind:value={gitSource.type}>
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitlab">GitLab</option>
|
||||
<option value="bitbucket">BitBucket</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2">
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
name="name"
|
||||
id="name"
|
||||
@@ -53,11 +51,9 @@
|
||||
bind:value={gitSource.name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="htmlUrl">HTML URL</label>
|
||||
<div class="col-span-2">
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="htmlUrl" class="text-base font-bold text-stone-100">HTML URL</label>
|
||||
<input
|
||||
type="url"
|
||||
name="htmlUrl"
|
||||
@@ -67,10 +63,8 @@
|
||||
bind:value={gitSource.htmlUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="apiUrl">API URL</label>
|
||||
<div class="col-span-2">
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="apiUrl" class="text-base font-bold text-stone-100">API URL</label>
|
||||
<input
|
||||
name="apiUrl"
|
||||
type="url"
|
||||
@@ -80,10 +74,15 @@
|
||||
bind:value={gitSource.apiUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3">
|
||||
<label for="organization" class="pt-2">Organization</label>
|
||||
<div class="col-span-2">
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<div class="flex flex-col">
|
||||
<label for="organization" class="pt-2 text-base font-bold text-stone-100"
|
||||
>Organization</label
|
||||
>
|
||||
<Explainer
|
||||
text="Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used."
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
name="organization"
|
||||
id="organization"
|
||||
@@ -91,11 +90,7 @@
|
||||
bind:value={gitSource.organization}
|
||||
bind:this={organizationEl}
|
||||
/>
|
||||
<Explainer
|
||||
text="Fill it if you would like to use an organization's as your Git Source. Otherwise your
|
||||
user will be used."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,56 +27,47 @@
|
||||
<div class="text-xl font-bold text-white">Configuration</div>
|
||||
<button type="submit" class="bg-orange-600 hover:bg-orange-500">Save</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="type">Type</label>
|
||||
|
||||
<div class="col-span-2">
|
||||
<select name="type" id="type" class="w-96" bind:value={gitSource.type}>
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitlab">GitLab</option>
|
||||
<option value="bitbucket">BitBucket</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="type" class="text-base font-bold text-stone-100">Type</label>
|
||||
<select name="type" id="type" class="w-96" bind:value={gitSource.type}>
|
||||
<option value="github">GitHub</option>
|
||||
<option value="gitlab">GitLab</option>
|
||||
<option value="bitbucket">BitBucket</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="name">Name</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="GitHub.com"
|
||||
required
|
||||
bind:this={nameEl}
|
||||
bind:value={gitSource.name}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<input
|
||||
name="name"
|
||||
id="name"
|
||||
placeholder="GitHub.com"
|
||||
required
|
||||
bind:this={nameEl}
|
||||
bind:value={gitSource.name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="htmlUrl">HTML URL</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
type="url"
|
||||
name="htmlUrl"
|
||||
id="htmlUrl"
|
||||
placeholder="eg: https://github.com"
|
||||
required
|
||||
bind:value={gitSource.htmlUrl}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="htmlUrl" class="text-base font-bold text-stone-100">HTML URL</label>
|
||||
<input
|
||||
type="url"
|
||||
name="htmlUrl"
|
||||
id="htmlUrl"
|
||||
placeholder="eg: https://github.com"
|
||||
required
|
||||
bind:value={gitSource.htmlUrl}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<label for="apiUrl">API URL</label>
|
||||
<div class="col-span-2">
|
||||
<input
|
||||
name="apiUrl"
|
||||
type="url"
|
||||
id="apiUrl"
|
||||
placeholder="eg: https://api.github.com"
|
||||
required
|
||||
bind:value={gitSource.apiUrl}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="apiUrl" class="text-base font-bold text-stone-100">API URL</label>
|
||||
<input
|
||||
name="apiUrl"
|
||||
type="url"
|
||||
id="apiUrl"
|
||||
placeholder="eg: https://api.github.com"
|
||||
required
|
||||
bind:value={gitSource.apiUrl}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
103
src/routes/register/index.svelte
Normal file
103
src/routes/register/index.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
export let userCount: number;
|
||||
|
||||
import { browser } from '$app/env';
|
||||
import { goto } from '$app/navigation';
|
||||
import { session } from '$app/stores';
|
||||
import { post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let loading = false;
|
||||
let emailEl;
|
||||
let email, password, passwordCheck;
|
||||
|
||||
if (browser && $session.userId) {
|
||||
goto('/');
|
||||
}
|
||||
onMount(() => {
|
||||
emailEl.focus();
|
||||
});
|
||||
async function handleSubmit() {
|
||||
if (password !== passwordCheck) {
|
||||
return errorNotification('Passwords do not match.');
|
||||
}
|
||||
loading = true;
|
||||
try {
|
||||
await post(`/login.json`, {
|
||||
email: email.toLowerCase(),
|
||||
password,
|
||||
isLogin: false
|
||||
});
|
||||
return window.location.replace('/');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="icons fixed top-0 left-0 m-3 cursor-pointer" on:click={() => goto('/')}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
<line x1="5" y1="12" x2="11" y2="18" />
|
||||
<line x1="5" y1="12" x2="11" y2="6" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex h-screen flex-col items-center justify-center">
|
||||
{#if $session.userId}
|
||||
<div class="flex justify-center px-4 text-xl font-bold">Already logged in...</div>
|
||||
{:else}
|
||||
<div class="flex justify-center px-4">
|
||||
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
|
||||
<div class="text-6xl font-bold border-gradient w-48 mx-auto border-b-4">Coolify</div>
|
||||
<div class="text-xs text-center font-bold pb-10">v{$session.version}</div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
autocomplete="off"
|
||||
required
|
||||
bind:this={emailEl}
|
||||
bind:value={email}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
bind:value={password}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="passwordCheck"
|
||||
placeholder="Password again"
|
||||
bind:value={passwordCheck}
|
||||
required
|
||||
/>
|
||||
|
||||
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
||||
<button type="submit" class="hover:bg-coollabs-100 text-white bg-coollabs"
|
||||
>Register</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{#if userCount === 0}
|
||||
<div class="pt-5">
|
||||
You are registering the first user. It will be the administrator of your Coolify instance.
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
11
src/routes/register/index.ts
Normal file
11
src/routes/register/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async () => {
|
||||
try {
|
||||
return { status: 200, body: { userCount: await db.prisma.user.count() } };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<div class="mx-auto max-w-4xl px-6 pb-12">
|
||||
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
||||
<div class="flex space-x-1 pb-5 font-bold">
|
||||
<div class="title">General</div>
|
||||
@@ -70,11 +70,7 @@
|
||||
>
|
||||
{/if}
|
||||
{#if service.type === 'plausibleanalytics' && isRunning}
|
||||
<button
|
||||
on:click|preventDefault={setEmailsToVerified}
|
||||
class:bg-pink-600={!loadingVerification}
|
||||
class:hover:bg-pink-500={!loadingVerification}
|
||||
disabled={loadingVerification}
|
||||
<button on:click|preventDefault={setEmailsToVerified} disabled={loadingVerification}
|
||||
>{loadingVerification ? 'Verifying' : 'Verify emails without SMTP'}</button
|
||||
>
|
||||
{/if}
|
||||
@@ -82,7 +78,7 @@
|
||||
|
||||
<div class="grid grid-flow-row gap-2">
|
||||
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||
<label for="name">Name</label>
|
||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||
<div>
|
||||
<input
|
||||
readonly={!$session.isAdmin}
|
||||
@@ -95,7 +91,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="destination">Destination</label>
|
||||
<label for="destination" class="text-base font-bold text-stone-100">Destination</label>
|
||||
<div>
|
||||
{#if service.destinationDockerId}
|
||||
<div class="no-underline">
|
||||
@@ -110,22 +106,23 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<label for="fqdn" class="pt-2">Domain (FQDN)</label>
|
||||
<div>
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://analytics.coollabs.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
<div class="flex-col ">
|
||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100">Domain (FQDN)</label>
|
||||
<Explainer
|
||||
text="If you specify <span class='text-pink-600 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-pink-600 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>
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://analytics.coollabs.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<Setting
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
title="Stop Service"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-pink-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Stop Service'
|
||||
: 'You do not have permission to stop the service.'}
|
||||
@@ -166,7 +166,7 @@
|
||||
title="Start Service"
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:bg-pink-600 hover:text-white"
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Start Service'
|
||||
: 'You do not have permission to start the service.'}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const post: RequestHandler = async (event) => {
|
||||
return {
|
||||
status: found ? 500 : 200,
|
||||
body: {
|
||||
error: found && `Domain ${getDomain(fqdn).replace('www.', '')} is already configured`
|
||||
error: found && `Domain ${getDomain(fqdn).replace('www.', '')} is already used.`
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -37,10 +37,16 @@
|
||||
import Services from './_Services/_Services.svelte';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
|
||||
export let service;
|
||||
export let isRunning;
|
||||
export let readOnly;
|
||||
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
||||
@@ -3,16 +3,8 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection,
|
||||
startHttpProxy,
|
||||
startTcpProxy
|
||||
} from '$lib/haproxy';
|
||||
import getPort from 'get-port';
|
||||
import { startHttpProxy } from '$lib/haproxy';
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
@@ -24,7 +16,6 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
type,
|
||||
@@ -35,14 +26,17 @@ export const post: RequestHandler = async (event) => {
|
||||
minio: { rootUser, rootUserPassword }
|
||||
} = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const data = await db.prisma.setting.findFirst();
|
||||
const { minPort, maxPort } = data;
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const publicPort = await getPort();
|
||||
|
||||
const publicPort = await getPort({ port: portNumbers(minPort, maxPort) });
|
||||
|
||||
const consolePort = 9001;
|
||||
const apiPort = 9000;
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
|
||||
const config = {
|
||||
@@ -90,15 +84,8 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: consolePort });
|
||||
await db.updateMinioService({ id, publicPort });
|
||||
await startHttpProxy(destinationDocker, id, publicPort, apiPort);
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getEngine, getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { checkContainer, configureSimpleServiceProxyOff, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import { checkContainer, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -21,7 +19,6 @@ export const post: RequestHandler = async (event) => {
|
||||
minio: { publicPort }
|
||||
} = service;
|
||||
await db.updateMinioService({ id, publicPort: null });
|
||||
const domain = getDomain(fqdn);
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
@@ -35,7 +32,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
try {
|
||||
await stopTcpHttpProxy(destinationDocker, publicPort);
|
||||
await configureSimpleServiceProxyOff(fqdn);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection
|
||||
} from '$lib/haproxy';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -21,13 +13,8 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, fqdn, destinationDockerId, destinationDocker } = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
|
||||
const { type, version, destinationDockerId, destinationDocker } = service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
|
||||
@@ -55,13 +42,6 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
const domain = getDomain(fqdn);
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
@@ -27,11 +24,6 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
await configureSimpleServiceProxyOff(fqdn);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -3,14 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection
|
||||
} from '$lib/haproxy';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -21,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
type,
|
||||
@@ -41,9 +32,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
} = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
|
||||
const config = {
|
||||
plausibleAnalytics: {
|
||||
image: `plausible/analytics:${version}`,
|
||||
@@ -82,7 +70,6 @@ export const post: RequestHandler = async (event) => {
|
||||
};
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
|
||||
@@ -186,13 +173,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d`
|
||||
);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: 8000 });
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
const domain = getDomain(fqdn);
|
||||
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
@@ -36,12 +33,6 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await configureSimpleServiceProxyOff(fqdn);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -3,14 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection
|
||||
} from '$lib/haproxy';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { getServiceImage, ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -21,12 +13,8 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, fqdn, destinationDockerId, destinationDocker } = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const { type, version, destinationDockerId, destinationDocker } = service;
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
@@ -73,13 +61,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
const domain = getDomain(fqdn);
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
@@ -27,13 +24,7 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
await configureSimpleServiceProxyOff(fqdn);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -3,14 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection
|
||||
} from '$lib/haproxy';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -21,20 +13,15 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
type,
|
||||
version,
|
||||
fqdn,
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
vscodeserver: { password }
|
||||
} = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
|
||||
@@ -83,13 +70,6 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: 8080 });
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { dockerInstance } from '$lib/docker';
|
||||
import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
const domain = getDomain(fqdn);
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
@@ -27,11 +24,6 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
await configureSimpleServiceProxyOff(fqdn);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 200
|
||||
|
||||
@@ -3,14 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { letsEncrypt } from '$lib/letsencrypt';
|
||||
import {
|
||||
checkHAProxy,
|
||||
configureSimpleServiceProxyOn,
|
||||
reloadHaproxy,
|
||||
setWwwRedirection
|
||||
} from '$lib/haproxy';
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -21,7 +13,6 @@ export const post: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
await checkHAProxy();
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
type,
|
||||
@@ -39,9 +30,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
} = service;
|
||||
|
||||
const domain = getDomain(fqdn);
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
|
||||
@@ -120,13 +108,6 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await configureSimpleServiceProxyOn({ id, domain, port: 80 });
|
||||
|
||||
if (isHttps) {
|
||||
await letsEncrypt({ domain, id });
|
||||
}
|
||||
await setWwwRedirection(fqdn);
|
||||
await reloadHaproxy(destinationDocker.engine);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user