Compare commits

..

66 Commits

Author SHA1 Message Date
Andras Bacsai
6311627899 Merge pull request #882 from coollabsio/next
v3.12.18
2023-01-24 15:09:51 +01:00
Andras Bacsai
37cea5fb61 update mattermost + traefik 2023-01-24 14:48:03 +01:00
Andras Bacsai
655a8cd60d feat: able to use $$ in traefik config gen
fix: repman icon
2023-01-24 13:52:39 +01:00
Andras Bacsai
4c8babc96a version++ 2023-01-23 10:45:19 +01:00
Andras Bacsai
612bacebed fix: cleanupStuckedContainers 2023-01-23 10:37:28 +01:00
Andras Bacsai
ade7c8566d fix: cleanupStuckedContainers 2023-01-23 10:37:14 +01:00
Andras Bacsai
19553ce5c8 Merge pull request #874 from coollabsio/next
v3.12.17
2023-01-20 21:14:06 +01:00
Andras Bacsai
18ed2527e8 fix 2023-01-20 20:51:37 +01:00
Andras Bacsai
b0652bc884 Merge pull request #872 from coollabsio/next
Next
2023-01-20 14:07:46 +01:00
Andras Bacsai
15c9ad23fe fix: stucked containers 2023-01-20 14:06:55 +01:00
Andras Bacsai
578bb12562 test new release gh action 2023-01-20 14:05:07 +01:00
Andras Bacsai
f82cfda07f version++ 2023-01-20 13:55:05 +01:00
Andras Bacsai
9e52b2788d Pocketbase GH release updated 2023-01-20 13:49:39 +01:00
Andras Bacsai
2e56a113d9 Test GH release thing 2023-01-20 13:48:57 +01:00
Andras Bacsai
4722d777e6 Merge pull request #871 from coollabsio/next
v3.12.15
2023-01-20 13:22:11 +01:00
Andras Bacsai
2141d54ae0 fix 2023-01-20 13:15:52 +01:00
Andras Bacsai
e346225136 fix 2023-01-20 13:10:40 +01:00
Andras Bacsai
012d4dae56 testing 2023-01-20 11:15:38 +01:00
Andras Bacsai
b4d9fe70af fix 2023-01-20 10:43:21 +01:00
Andras Bacsai
85e83b5441 Test new gh actions 2023-01-20 10:42:13 +01:00
Andras Bacsai
6b2a453b8f fix: deletion + cleanupStuckedContainers 2023-01-20 10:10:36 +01:00
Andras Bacsai
27021538d8 fix: cleanup stucked containers 2023-01-20 09:40:29 +01:00
Andras Bacsai
8b57a2b055 fix: cleanup function 2023-01-20 09:26:48 +01:00
Andras Bacsai
75dd894685 Merge pull request #867 from coollabsio/next
v3.12.14
2023-01-19 14:36:05 +01:00
Andras Bacsai
9101ef8774 version++ 2023-01-19 14:33:33 +01:00
Andras Bacsai
5932540630 fix: www redirect 2023-01-19 14:33:20 +01:00
Andras Bacsai
ec376b2e47 Merge pull request #864 from coollabsio/next
v3.12.13
2023-01-18 19:00:03 +01:00
Andras Bacsai
a176562ad0 fix: secrets 2023-01-18 18:51:03 +01:00
Andras Bacsai
becf37b676 Merge pull request #858 from coollabsio/next
v3.12.12
2023-01-17 12:33:51 +01:00
Andras Bacsai
9b5efab8f8 fix: grpc 2023-01-17 11:51:53 +01:00
Andras Bacsai
e98a8ba599 traefik dashbord in dev 2023-01-17 11:12:52 +01:00
Andras Bacsai
7ddac50008 feat: http + h2c paralel 2023-01-17 11:12:42 +01:00
Andras Bacsai
9837ae359f feat: init h2c (http2/grpc) support 2023-01-17 10:35:04 +01:00
Andras Bacsai
710a829dcb version++ 2023-01-17 10:00:50 +01:00
Andras Bacsai
ccd84fa454 fix: build args docker compose 2023-01-17 10:00:27 +01:00
Andras Bacsai
335b36d3a9 Merge pull request #857 from zek/patch-2
Fix docker-compose build args
2023-01-17 09:26:12 +01:00
Talha Zekeriya Durmuş
2be30fae00 Handle string build parameter 2023-01-17 02:11:06 +01:00
Talha Zekeriya Durmuş
db5cd21884 Fix docker-compose build args 2023-01-17 02:04:01 +01:00
Andras Bacsai
bfd3020031 Merge pull request #853 from coollabsio/next
v3.12.11
2023-01-16 12:44:04 +01:00
Andras Bacsai
344c36997a fix: public gh repo reload compose 2023-01-16 12:36:59 +01:00
Andras Bacsai
dfd9272b70 version ++ 2023-01-16 12:17:48 +01:00
Andras Bacsai
359f4520f5 test template + tags during dev 2023-01-16 11:45:45 +01:00
Andras Bacsai
aecf014f4e Merge pull request #843 from zek/soketi-logo
Add Soketi Logo
2023-01-16 11:10:48 +01:00
Andras Bacsai
d2a89ddf84 Merge pull request #844 from zek/repman-logo
Add Repman logo
2023-01-16 11:10:35 +01:00
Andras Bacsai
c01fe153ae Merge pull request #847 from zek/mattermost
Add Matermost Logo
2023-01-16 11:10:14 +01:00
Andras Bacsai
4f4a838799 update templates+tags 2023-01-16 10:58:44 +01:00
Andras Bacsai
ac6f2567eb fix: build env variables with docker compose 2023-01-16 10:42:23 +01:00
Andras Bacsai
05a5816ac6 fix: do not cleanup compose applications as unconfigured 2023-01-16 10:22:14 +01:00
Andras Bacsai
9c8f6e9195 fix: delete apps with previews 2023-01-16 10:16:49 +01:00
Andras Bacsai
2fd001f6d2 fix: docker log sequence 2023-01-16 10:06:41 +01:00
Andras Bacsai
d641d32413 fix: compose file location 2023-01-16 09:48:15 +01:00
Andras Bacsai
18064ef6a2 fixes related to docker-compose 2023-01-16 09:44:08 +01:00
Andras Bacsai
5cb9216add wip: trpc 2023-01-13 15:50:20 +01:00
Andras Bacsai
91c36dc810 wip: trpc 2023-01-13 15:24:43 +01:00
Andras Bacsai
6efb02fa32 wip: trpc 2023-01-13 15:21:54 +01:00
Andras Bacsai
97313e4180 wip: trpc 2023-01-13 14:54:21 +01:00
Andras Bacsai
568ab24fd9 wip: trpc 2023-01-13 14:17:36 +01:00
Talha Zekeriya Durmuş
5a745efcd3 Add Matermost Logo 2023-01-13 02:38:33 +01:00
Andras Bacsai
c651570e62 wip: trpc 2023-01-12 16:50:17 +01:00
Andras Bacsai
8980598085 wip: trpc 2023-01-12 16:43:41 +01:00
Talha Zekeriya Durmuş
c07c742feb Add Repman logo 2023-01-12 01:45:13 +01:00
Talha Zekeriya Durmuş
1053abb9a9 Add Soketi Logo 2023-01-12 01:41:35 +01:00
Andras Bacsai
2c9e57cbb1 Merge pull request #841 from coollabsio/next
v3.12.10
2023-01-11 11:44:22 +01:00
Andras Bacsai
c6eaa2c8a6 update packages in api 2023-01-11 11:35:57 +01:00
Andras Bacsai
5ab5e913ee Merge pull request #840 from zek/patch-1
Fix: add missing variables
2023-01-11 11:33:17 +01:00
Talha Zekeriya Durmuş
cea53ca476 Fix: add missing variables 2023-01-11 11:12:44 +01:00
460 changed files with 30058 additions and 2899 deletions

View File

@@ -5,7 +5,9 @@ on:
paths:
- "others/pocketbase/*"
- ".github/workflows/pocketbase-release.yml"
branches:
- next
- main
jobs:
arm64:
runs-on: [self-hosted, arm64]

View File

@@ -54,7 +54,7 @@ jobs:
context: .
platforms: linux/amd64
push: true
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}
cache-from: type=registry,ref=coollabsio/coolify:buildcache-amd64
cache-to: type=registry,ref=coollabsio/coolify:buildcache-amd64,mode=max
aarch64:
@@ -103,10 +103,10 @@ jobs:
id: package-version
- name: Create & publish manifest
run: |
docker manifest create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
docker manifest create coollabsio/coolify:latest --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64
docker manifest push coollabsio/coolify:${{steps.package-version.outputs.current-version}}
docker manifest push coollabsio/coolify:latest
docker buildx imagetools create --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 --tag coollabsio/coolify:${{steps.package-version.outputs.current-version}}
docker tag coollabsio/coolify:${{steps.package-version.outputs.current-version}} coollabsio/coolify:latest
docker push coollabsio/coolify:latest
docker buildx imagetools create --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 --tag coollabsio/coolify:latest
- uses: sarisia/actions-status-discord@v1
if: always()
with:

View File

@@ -65,7 +65,7 @@ jobs:
context: .
platforms: linux/amd64
push: true
tags: coollabsio/coolify:next-amd64
tags: coollabsio/coolify:next
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-amd64
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max
merge-manifest:
@@ -85,8 +85,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create & publish manifest
run: |
docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64
docker manifest push coollabsio/coolify:next
docker buildx imagetools create --append coollabsio/coolify:next-arm64 --tag coollabsio/coolify:next
- uses: sarisia/actions-status-discord@v1
if: always()
with:

4
apps/api/.gitignore vendored
View File

@@ -8,4 +8,6 @@ package
!.env.example
dist
dev.db
client
client
testTemplate.yaml
testTags.json

View File

@@ -1,11 +1,9 @@
[
{ "name": "directus-postgresql", "image": "directus/directus", "tags": ["9.22"] },
{ "name": "whoogle", "image": "benbusby/whoogle-search", "tags": ["0.8.1"] },
{ "name": "libretranslate", "image": "libretranslate/libretranslate", "tags": ["v1.3.8"] },
{
"name": "appsmith",
"image": "appsmith/appsmith-ce",
"tags": [
"v1.9.3",
"v1.9.1",
"v1.8.15",
"v1.8.12",
@@ -34,8 +32,7 @@
"v1.6.5",
"v1.6.3",
"v1.6.1",
"v1.5.30",
"v1.5.28"
"v1.5.30"
]
},
{
@@ -74,6 +71,42 @@
"0.3.1"
]
},
{
"name": "directus-postgresql",
"image": "directus/directus",
"tags": [
"9.22.3",
"9.22.0",
"9.21.0",
"9.20.4",
"9.20.2",
"9.20.0",
"9.19.2",
"9.18.0",
"9.17.4",
"9.17.2",
"9.17.0",
"9.16.0",
"9.15.0",
"9.14.5",
"9.14.3",
"9.14.0",
"9.13.0",
"9.12.2",
"9.12.0",
"9.11.0",
"9.10.0",
"9.9.0",
"9.8.0",
"9.7.0",
"9.6.0",
"9.5.2",
"9.5.0",
"9.4.2",
"9.4.0",
"9.3.0"
]
},
{
"name": "fider",
"image": "getfider/fider",
@@ -114,6 +147,9 @@
"name": "ghost-mariadb",
"image": "bitnami/ghost",
"tags": [
"5.30.1",
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -141,9 +177,6 @@
"5.22.4",
"5.22.3",
"5.22.2",
"5.22.1",
"5.22.0",
"5.21.0",
"4.48.8"
]
},
@@ -151,6 +184,8 @@
"name": "ghost-mysql",
"image": "library/ghost",
"tags": [
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -178,15 +213,15 @@
"5.17.2",
"5.17.1",
"5.17.0",
"5.16.2",
"5.14.2",
"5.14.1"
"5.16.2"
]
},
{
"name": "ghost-only",
"image": "library/ghost",
"tags": [
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -214,9 +249,7 @@
"5.17.2",
"5.17.1",
"5.17.0",
"5.16.2",
"5.14.2",
"5.14.1"
"5.16.2"
]
},
{
@@ -373,6 +406,7 @@
"7.0.0",
"6.0.1",
"6.0.0",
"20.0.3",
"20.0.2",
"20.0.1",
"20.0.0",
@@ -404,38 +438,12 @@
{
"name": "lavalink",
"image": "fredboat/lavalink",
"tags": [
"v3.7",
"v3.6",
"v3-vda0b3a4b3916a7b1a2b79702de1143c3a6939810-SNAPSHOT",
"v3-vc92690c425390bd20f6c51643c67ba79ab85b7e0-SNAPSHOT",
"v3-vab81dcd46adf3e8a961dd57eacd2a1bde1233e6c-SNAPSHOT",
"v3-v9c9432704d6a4badfcbd06a57597c54bed8f4326-SNAPSHOT",
"v3-v3.0",
"v3-v3",
"v3-v124f8fae7dab299f9cdf1cb4c1715be455497286-SNAPSHOT",
"v3-",
"v3",
"v2.0.1",
"v2.0",
"v2",
"update-udpqueue-vb4a439d6147dbd8641ea4f265e8efc9f1e16e2d3-SNAPSHOT",
"update-udpqueue-",
"update-udpqueue",
"revert-713-fix-error-for-loading-jda-nas",
"refactor-github-actions",
"patch-update-lp",
"patch-update-github-actions",
"patch-more-configurable-github-actions",
"patch-lavaplayer-update",
"patch-lavaplayer-bump",
"patch-build-number",
"next-api-vd4db194cac7a839a3899857f1f6d7b910369309d-SNAPSHOT",
"next-api-vc2e018d5ffef54b2d17244b3d213e31723a084d6-SNAPSHOT",
"next-api-v42cb5f7c58e98d1911e87bffb35aee0a235b85f8-SNAPSHOT",
"next-api-v31a243bda80badbd7d643f68fc1f87e99639060f-SNAPSHOT",
"next-api-v17f6884434c2d70d1704b2322a951d9f07af8865-SNAPSHOT"
]
"tags": ["3.7.0", "3.6.1", "3.5.1", "v2.0.1"]
},
{
"name": "libretranslate",
"image": "libretranslate/libretranslate",
"tags": ["v1.3.8", "v1.3.6", "v1.3.4", "v1.3.2", "v1.3.0", "v1.2.8"]
},
{
"name": "meilisearch",
@@ -477,6 +485,7 @@
"name": "minio",
"image": "minio/minio",
"tags": [
"RELEASE.2023-01-12T02-06-16Z",
"RELEASE.2023-01-06T18-11-18Z",
"RELEASE.2023-01-02T09-40-09Z",
"RELEASE.2022-12-12T19-27-27Z",
@@ -505,8 +514,7 @@
"RELEASE.2022-09-01T23-53-36Z.fips",
"RELEASE.2022-08-26T19-53-15Z.fips",
"RELEASE.2022-08-25T07-17-05Z.fips",
"RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb",
"RELEASE.2022-08-22T23-53-06Z"
"RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb"
]
},
{
@@ -549,77 +557,56 @@
"name": "nocodb",
"image": "nocodb/nocodb",
"tags": [
"0.101.2",
"0.101.0",
"0.100.1",
"0.99.1",
"0.98.4",
"0.98.2",
"0.98.0",
"0.96.4",
"0.96.2",
"0.96.0",
"0.92.3",
"0.91.10",
"0.91.9",
"0.91.7",
"0.91.0",
"0.90.10",
"0.90.7",
"0.90.4",
"0.90.2",
"0.90.0",
"0.84.15",
"0.84.12",
"0.84.8",
"0.84.6",
"0.84.2",
"0.84.1",
"0.83.6",
"0.83.3",
"0.83.1",
"0.82.0",
"0.81.0",
"0.11.46"
"0.99.2",
"0.99.0",
"0.98.3",
"0.98.1",
"0.97.0",
"0.96.3",
"0.96.1",
"0.92.4",
"0.92.0",
"0.91.8",
"0.91.6",
"0.91.1",
"0.90.11",
"0.90.8",
"0.90.5",
"0.90.3",
"0.90.1",
"0.84.16",
"0.84.14",
"0.84.10",
"0.84.9",
"0.84.7",
"0.84.3",
"0.83.8",
"0.83.5",
"0.83.2",
"0.83.0"
]
},
{
"name": "openblocks",
"image": "openblocksdev/openblocks-ce",
"tags": ["latest", "heroku", "beta", "1.1.3", "1.1.2", "1.1.1", "1.1.0", "1.0.21"]
"tags": ["1.1.3", "1.1.1", "1.0.21"]
},
{
"name": "plausibleanalytics-arm",
"image": "plausible/analytics",
"tags": [
"v1.5.1",
"v1.5.0-rc.2",
"v1.5.0-rc.1",
"v1.5.0",
"v1.5",
"v1.4.4",
"v1.4.3",
"v1.4.2",
"v1.4.1",
"v1.4.0.rc.0",
"v1.4.0-rc.0",
"v1.4.0",
"v1.4",
"v1.3.0-rc.1",
"v1.3.0-rc.0",
"v1.3.0",
"v1.3",
"v1.2.1",
"v1.2.0",
"v1.2-rc.1",
"v1.2-rc.0",
"v1.2",
"v1.1.1",
"v1.1.0",
"v1.1",
"v1.0.0",
"v1.0",
"v1",
"stable",
"master"
"v1.0.0"
]
},
{
@@ -627,55 +614,26 @@
"image": "plausible/analytics",
"tags": [
"v1.5.1",
"v1.5.0-rc.2",
"v1.5.0-rc.1",
"v1.5.0",
"v1.5",
"v1.4.4",
"v1.4.3",
"v1.4.2",
"v1.4.1",
"v1.4.0.rc.0",
"v1.4.0-rc.0",
"v1.4.0",
"v1.4",
"v1.3.0-rc.1",
"v1.3.0-rc.0",
"v1.3.0",
"v1.3",
"v1.2.1",
"v1.2.0",
"v1.2-rc.1",
"v1.2-rc.0",
"v1.2",
"v1.1.1",
"v1.1.0",
"v1.1",
"v1.0.0",
"v1.0",
"v1",
"stable",
"master"
]
},
{
"name": "pocketbase",
"image": "coollabsio/pocketbase",
"tags": [
"0.8.0-arm64",
"0.8.0-amd64",
"0.8.0-aarch64",
"0.8.0",
"0.10.2-arm64",
"0.10.2-amd64",
"0.10.2-aarch64",
"0.10.2"
"v1.0.0"
]
},
{ "name": "pocketbase", "image": "coollabsio/pocketbase", "tags": ["0.11.0", "0.10.2", "0.8.0"] },
{
"name": "searxng",
"image": "searxng/searxng",
"tags": [
"2023.01.15-52d41559",
"2023.01.15-13b0c251",
"2023.01.14-b720a495",
"2023.01.14-449aebae",
"2023.01.14-18d895ff",
"2023.01.09-afd71a6c",
"2023.01.09-a90ed481",
"2023.01.08-54e63839",
@@ -700,18 +658,14 @@
"2022.12.26-0d489617",
"2022.12.23-e8f72d70",
"2022.12.23-a2d506d4",
"2022.12.22-d75ae7c8",
"2022.12.16-f5bd73d9",
"2022.12.16-b9274821",
"2022.12.16-42ca37a6",
"2022.12.16-2a51c856",
"2022.12.16-0dac581c"
"2022.12.22-d75ae7c8"
]
},
{
"name": "trilium",
"image": "zadam/trilium",
"tags": [
"0.58.4",
"0.57.4",
"0.57.2",
"0.56.1",
@@ -740,8 +694,7 @@
"0.45.7",
"0.45.5",
"0.45.3",
"0.44.8",
"0.44.6"
"0.44.8"
]
},
{
@@ -907,6 +860,15 @@
"image": "weblate/weblate",
"tags": [
"latest",
"edge-2023-01-13-e824b551f23c3679467e38b06366744a06aa3b0c",
"edge-2023-01-13-468b996565e6b62edb78d40b515c476e0d860273",
"edge-2023-01-12-fe3d58b14f119eb5501220e9f096949c2e1ec2d3",
"edge-2023-01-12-112f75f9ee9e118ad493215f89742e6e091be8d0",
"edge-2023-01-11-f7bb190993e329d1529694e8cc7f5e0a80ccd615",
"edge-2023-01-11-e8ef3183aa7723f32c2b60c7c3b89910f2c7c593",
"edge-2023-01-11-155231f6cde18a65e3f35093d66dd0ce93aa7154",
"edge-2023-01-10-e47516e4022f87c019e61998b556b69111187aa9",
"edge-2023-01-10-98c6b38c746165adb27b2a8e93a74fa9ab64f17c",
"edge-2023-01-10-1df5c9dd96a6d8650f6881942fecbe33e1884295",
"edge-2023-01-09-7029b7b6c630be7cdac07d1629573dd2b81bc05f",
"edge-2023-01-09-4b05a878aa25b2c544a4e77027769b5934ec561f",
@@ -926,16 +888,24 @@
"edge-2022-12-24-3e1503494ce06ad6ff32f02db1a7d59224e5c860",
"edge-2022-12-21-cac4b09f943fe97700e3a33b7caf23277d2fcc11",
"edge-2022-12-21-3a8dd1bf66a7295f3512346bc1c97d55c5649dcf",
"edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099",
"edge-2022-12-16-318a467d2e529a081e9ea9dbad993c1736ff1a00",
"edge-2022-12-16-1af41ec4bd3838f967d88b68dec8195419e01e6f",
"edge-2022-12-16-02e9d020b01d004655c3af20c68a30f6c4645c1a",
"edge-2022-12-15-a6af1384a0831b17c43da7262f80d0cfbc766835",
"edge-2022-12-15-a1c9f77b301a9e23fc05ef2adc4694cceb632c25",
"edge-2022-12-15-1305f7115ef79b75e638b097772680d9cadbd4d0",
"edge-2022-12-14-b400145f05687e647bd4c8192be99f7f04373fb5",
"edge-2022-12-12-c0db193a3baacd107c5f2c28c6e0af89c3d5afa3",
"edge-2022-12-09-647d40c67cf405870ba71a01584a42cfaec5915f"
"edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099"
]
},
{
"name": "whoogle",
"image": "benbusby/whoogle-search",
"tags": [
"0.8.0",
"0.7.3",
"0.7.1",
"0.6.0",
"0.5.3",
"0.5.1",
"0.4.1",
"0.3.2",
"v0.3.0",
"0.1.2",
"0.1.0"
]
},
{

View File

@@ -111,7 +111,7 @@
defaultValue: $$generate_password
description: "Secret string for the project."
showOnConfiguration: true
- templateVersion: 1.0.0
defaultVersion: v1.3.8
documentation: https://github.com/LibreTranslate/LibreTranslate
@@ -166,7 +166,7 @@
defaultValue: "false"
description: "Disable or enable web ui. True or false."
- templateVersion: 1.0.0
defaultVersion: 0.8.1
defaultVersion: 0.8.0
documentation: https://github.com/benbusby/whoogle-search
type: whoogle
name: Whoogle Search
@@ -223,7 +223,7 @@
ports:
- "3000"
- templateVersion: 1.0.0
defaultVersion: "0.10.2"
defaultVersion: "0.11.0"
documentation: https://pocketbase.io/docs/
type: pocketbase
name: Pocketbase
@@ -381,7 +381,7 @@
defaultValue: plausible.js
description: This is the default script name.
- templateVersion: 1.0.0
defaultVersion: "1.17"
defaultVersion: "1.18"
documentation: https://docs.gitea.io
type: gitea
name: Gitea
@@ -594,7 +594,7 @@
defaultValue: $$generate_password
required: true
- templateVersion: 1.0.0
defaultVersion: v1.8.9
defaultVersion: v1.9.3
documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/
type: appsmith
name: Appsmith
@@ -627,7 +627,7 @@
defaultValue: "true"
description: ""
- templateVersion: 1.0.0
defaultVersion: 0.57.4
defaultVersion: 0.58.4
documentation: https://hub.docker.com/r/zadam/trilium
description: "A hierarchical note taking application with focus on building large personal knowledge bases."
labels:
@@ -647,7 +647,7 @@
- "8080"
variables: []
- templateVersion: 1.0.0
defaultVersion: 1.18.5
defaultVersion: 1.19.4
documentation: https://hub.docker.com/r/louislam/uptime-kuma
description: A free & fancy self-hosted monitoring tool.
labels:
@@ -664,7 +664,7 @@
- "3001"
variables: []
- templateVersion: 1.0.0
defaultVersion: "5.8"
defaultVersion: "6.0"
documentation: https://hub.docker.com/r/silviof/docker-languagetool
description: "A multilingual grammar, style and spell checker."
type: languagetool
@@ -679,7 +679,7 @@
- "8010"
variables: []
- templateVersion: 1.0.0
defaultVersion: 1.26.0
defaultVersion: 1.27.0
documentation: https://hub.docker.com/r/vaultwarden/server
description: "Bitwarden compatible server written in Rust."
type: vaultwarden
@@ -697,7 +697,7 @@
- "80"
variables: []
- templateVersion: 1.0.0
defaultVersion: 9.3.1
defaultVersion: 9.3.2
documentation: https://hub.docker.com/r/grafana/grafana
type: grafana
name: Grafana
@@ -718,7 +718,7 @@
- "3000"
variables: []
- templateVersion: 1.0.0
defaultVersion: 1.1.2
defaultVersion: 1.2.0
documentation: https://appwrite.io/docs
type: appwrite
name: Appwrite
@@ -1888,7 +1888,7 @@
defaultValue: weblate
description: ""
- templateVersion: 1.0.0
defaultVersion: 2022.12.12-966e9c3c
defaultVersion: 2023.01.15-52d41559
documentation: https://docs.searxng.org/
type: searxng
name: SearXNG
@@ -1961,7 +1961,7 @@
defaultValue: $$generate_password
description: ""
- templateVersion: 1.0.0
defaultVersion: v3.0.0
defaultVersion: v3.0.2
documentation: https://glitchtip.com/documentation
type: glitchtip
name: GlitchTip
@@ -2183,7 +2183,7 @@
defaultValue: glitchtip
description: ""
- templateVersion: 1.0.0
defaultVersion: v2.16.0
defaultVersion: v2.16.1
documentation: https://hasura.io/docs/latest/index/
type: hasura
name: Hasura
@@ -2663,7 +2663,7 @@
description: ""
showOnConfiguration: true
- templateVersion: 1.0.0
defaultVersion: v0.30.1
defaultVersion: v0.30.5
documentation: https://docs.meilisearch.com/learn/getting_started/quick_start.html
type: meilisearch
name: MeiliSearch
@@ -2693,7 +2693,7 @@
showOnConfiguration: true
- templateVersion: 1.0.0
ignore: true
defaultVersion: latest
defaultVersion: 5.30.0
documentation: https://docs.ghost.org
arch: amd64
type: ghost-mariadb
@@ -2811,7 +2811,7 @@
defaultValue: $$generate_password
description: ""
- templateVersion: 1.0.0
defaultVersion: "5.25.3"
defaultVersion: 5.30.0
documentation: https://docs.ghost.org
type: ghost-only
name: Ghost
@@ -2875,7 +2875,7 @@
placeholder: "ghost_db"
required: true
- templateVersion: 1.0.0
defaultVersion: "5.25.3"
defaultVersion: 5.30.0
documentation: https://docs.ghost.org
type: ghost-mysql
name: Ghost
@@ -2952,7 +2952,7 @@
defaultValue: $$generate_password
description: ""
- templateVersion: 1.0.0
defaultVersion: php8.1
defaultVersion: php8.2
documentation: https://wordpress.org/
type: wordpress
name: WordPress
@@ -3042,7 +3042,7 @@
description: ""
readOnly: true
- templateVersion: 1.0.0
defaultVersion: php8.1
defaultVersion: php8.2
documentation: https://wordpress.org/
type: wordpress-only
name: WordPress
@@ -3116,7 +3116,7 @@
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', 0);
- templateVersion: 1.0.0
defaultVersion: 4.9.0
defaultVersion: 4.9.1
documentation: https://coder.com/docs/coder-oss/latest
type: vscodeserver
name: VSCode Server
@@ -3131,7 +3131,6 @@
depends_on: []
image: "codercom/code-server:$$core_version"
volumes:
- "$$id-config-data:/home/coder/.local/share/code-server"
- "$$id-vscodeserver-data:/home/coder"
- "$$id-keys-directory:/root/.ssh"
- "$$id-theme-and-plugin-directory:/root/.local/share/code-server"
@@ -3147,7 +3146,7 @@
description: ""
showOnConfiguration: true
- templateVersion: 1.0.0
defaultVersion: RELEASE.2022-12-12T19-27-27Z
defaultVersion: RELEASE.2023-01-12T02-06-16Z
documentation: https://min.io/docs/minio
type: minio
name: MinIO
@@ -3206,7 +3205,7 @@
description: ""
showOnConfiguration: true
- templateVersion: 1.0.0
defaultVersion: 0.21.1
defaultVersion: stable
documentation: https://fider.io/docs
type: fider
name: Fider
@@ -3325,7 +3324,7 @@
defaultValue: $$generate_username
description: ""
- templateVersion: 1.0.0
defaultVersion: 0.207.0
defaultVersion: 0.210.1
documentation: https://docs.n8n.io
type: n8n
name: n8n.io
@@ -3356,7 +3355,7 @@
defaultValue: $$generate_fqdn
description: ""
- templateVersion: 1.0.0
defaultVersion: stable
defaultVersion: v1.5.1
documentation: https://plausible.io/doc/
arch: amd64
type: plausibleanalytics
@@ -3502,7 +3501,7 @@
defaultValue: plausible.js
description: This is the default script name.
- templateVersion: 1.0.0
defaultVersion: 0.99.1
defaultVersion: 0.101.2
documentation: https://docs.nocodb.com
type: nocodb
name: NocoDB

View File

@@ -16,31 +16,31 @@
},
"dependencies": {
"@breejs/ts-worker": "2.0.0",
"@fastify/autoload": "5.5.0",
"@fastify/autoload": "5.7.0",
"@fastify/cookie": "8.3.0",
"@fastify/cors": "8.2.0",
"@fastify/env": "4.1.0",
"@fastify/jwt": "6.3.3",
"@fastify/env": "4.2.0",
"@fastify/jwt": "6.5.0",
"@fastify/multipart": "7.3.0",
"@fastify/static": "6.5.1",
"@fastify/static": "6.6.0",
"@iarna/toml": "2.2.5",
"@ladjs/graceful": "3.0.2",
"@prisma/client": "4.6.1",
"@sentry/node": "7.21.1",
"@sentry/tracing": "7.21.1",
"axe": "11.0.0",
"@ladjs/graceful": "3.2.1",
"@prisma/client": "4.8.1",
"@sentry/node": "7.30.0",
"@sentry/tracing": "7.30.0",
"axe": "11.2.1",
"bcryptjs": "2.4.3",
"bree": "9.1.2",
"cabin": "11.0.1",
"bree": "9.1.3",
"cabin": "11.1.1",
"compare-versions": "5.0.1",
"csv-parse": "5.3.2",
"csv-parse": "5.3.3",
"csvtojson": "2.0.10",
"cuid": "2.1.8",
"dayjs": "1.11.6",
"dayjs": "1.11.7",
"dockerode": "3.3.4",
"dotenv-extended": "2.9.0",
"execa": "6.1.0",
"fastify": "4.10.2",
"fastify": "4.11.0",
"fastify-plugin": "4.3.0",
"fastify-socket.io": "4.0.0",
"generate-password": "1.7.0",
@@ -54,30 +54,30 @@
"node-os-utils": "1.3.7",
"p-all": "4.0.0",
"p-throttle": "5.0.0",
"prisma": "4.6.1",
"prisma": "4.8.1",
"public-ip": "6.0.1",
"pump": "3.0.0",
"shell-quote": "^1.7.4",
"socket.io": "4.5.3",
"ssh-config": "4.1.6",
"socket.io": "4.5.4",
"ssh-config": "4.2.0",
"strip-ansi": "7.0.1",
"unique-names-generator": "4.7.1"
},
"devDependencies": {
"@types/node": "18.11.9",
"@types/node": "18.11.18",
"@types/node-os-utils": "1.3.0",
"@typescript-eslint/eslint-plugin": "5.44.0",
"@typescript-eslint/parser": "5.44.0",
"esbuild": "0.15.15",
"eslint": "8.28.0",
"eslint-config-prettier": "8.5.0",
"@typescript-eslint/eslint-plugin": "5.48.1",
"@typescript-eslint/parser": "5.48.1",
"esbuild": "0.16.16",
"eslint": "8.31.0",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-prettier": "4.2.1",
"nodemon": "2.0.20",
"prettier": "2.7.1",
"prettier": "2.8.2",
"rimraf": "3.0.2",
"tsconfig-paths": "4.1.0",
"tsconfig-paths": "4.1.2",
"types-fastify-socket.io": "0.0.1",
"typescript": "4.9.3"
"typescript": "4.9.4"
},
"prisma": {
"seed": "node prisma/seed.js"

View File

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

View File

@@ -186,6 +186,7 @@ model ApplicationSettings {
isPublicRepository Boolean @default(false)
isDBBranching Boolean @default(false)
isCustomSSL Boolean @default(false)
isHttp2 Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
application Application @relation(fields: [applicationId], references: [id])

View File

@@ -171,6 +171,11 @@ const host = '0.0.0.0';
await cleanupStorage();
}, 60000 * 15);
// Cleanup stucked containers (not defined in Coolify, but still running and managed by Coolify)
setInterval(async () => {
await cleanupStuckedContainers();
}, 60000);
// checkProxies, checkFluentBit & refresh templates
setInterval(async () => {
await checkProxies();
@@ -197,7 +202,13 @@ const host = '0.0.0.0';
await copySSLCertificates();
}, 10000);
await Promise.all([getTagsTemplates(), getArch(), getIPAddress(), configureRemoteDockers()]);
await Promise.all([
getTagsTemplates(),
getArch(),
getIPAddress(),
configureRemoteDockers()
// cleanupStuckedContainers()
]);
} catch (error) {
console.error(error);
process.exit(1);
@@ -225,8 +236,22 @@ async function getTagsTemplates() {
const { default: got } = await import('got');
try {
if (isDev) {
const templates = await fs.readFile('./devTemplates.yaml', 'utf8');
const tags = await fs.readFile('./devTags.json', 'utf8');
let templates = await fs.readFile('./devTemplates.yaml', 'utf8');
let tags = await fs.readFile('./devTags.json', 'utf8');
try {
if (await fs.stat('./testTemplate.yaml')) {
templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8'));
}
} catch (error) {}
try {
if (await fs.stat('./testTags.json')) {
const testTags = await fs.readFile('./testTags.json', 'utf8');
if (testTags.length > 0) {
tags = JSON.stringify(JSON.parse(tags).concat(JSON.parse(testTags)));
}
}
} catch (error) {}
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates)));
await fs.writeFile('./tags.json', tags);
console.log('[004] Tags and templates loaded in dev mode...');
@@ -297,6 +322,49 @@ async function getArch() {
} catch (error) {}
}
async function cleanupStuckedContainers() {
try {
const destinationDockers = await prisma.destinationDocker.findMany();
let enginesDone = new Set();
for (const destination of destinationDockers) {
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress))
return;
if (destination.engine) {
enginesDone.add(destination.engine);
}
if (destination.remoteIpAddress) {
if (!destination.remoteVerified) continue;
enginesDone.add(destination.remoteIpAddress);
}
const { stdout: containers } = await executeCommand({
dockerId: destination.id,
command: `docker container ps -a --filter "label=coolify.managed=true" --format '{{ .Names}}'`
});
if (containers) {
const containersArray = containers.trim().split('\n');
if (containersArray.length > 0) {
for (const container of containersArray) {
const containerId = container.split('-')[0];
const application = await prisma.application.findFirst({
where: { id: { startsWith: containerId } }
});
const service = await prisma.service.findFirst({
where: { id: { startsWith: containerId } }
});
const database = await prisma.database.findFirst({
where: { id: { startsWith: containerId } }
});
if (!application && !service && !database) {
await executeCommand({ command: `docker container rm -f ${container}` });
}
}
}
}
}
} catch (error) {
console.log(error);
}
}
async function configureRemoteDockers() {
try {
const remoteDocker = await prisma.destinationDocker.findMany({
@@ -529,9 +597,13 @@ async function cleanupStorage() {
let enginesDone = new Set();
for (const destination of destinationDockers) {
if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return;
if (destination.engine) enginesDone.add(destination.engine);
if (destination.remoteIpAddress) enginesDone.add(destination.remoteIpAddress);
let force = false;
if (destination.engine) {
enginesDone.add(destination.engine);
}
if (destination.remoteIpAddress) {
if (!destination.remoteVerified) continue;
enginesDone.add(destination.remoteIpAddress);
}
let lowDiskSpace = false;
try {
let stdout = null;
@@ -577,6 +649,8 @@ async function cleanupStorage() {
}
}
} catch (error) {}
await cleanupDockerStorage(destination.id, lowDiskSpace, force);
if (lowDiskSpace) {
await cleanupDockerStorage(destination.id);
}
}
}

View File

@@ -196,7 +196,7 @@ import * as buildpacks from '../lib/buildPacks';
await executeCommand({
debug: true,
dockerId: destinationDocker.id,
command: `docker compose --project-directory ${workdir} up -d`
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
});
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
} catch (error) {
@@ -601,6 +601,7 @@ import * as buildpacks from '../lib/buildPacks';
}
if (buildPack === 'compose') {
const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`;
try {
const { stdout: containers } = await executeCommand({
dockerId: destinationDockerId,
@@ -630,7 +631,7 @@ import * as buildpacks from '../lib/buildPacks';
buildId,
applicationId,
dockerId: destinationDocker.id,
command: `docker compose --project-directory ${workdir} up -d`
command: `docker compose --project-directory ${workdir} -f ${fileYaml} up -d`
});
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
await prisma.build.update({
@@ -725,7 +726,7 @@ import * as buildpacks from '../lib/buildPacks';
await executeCommand({
debug,
dockerId: destinationDocker.id,
command: `docker compose --project-directory ${workdir} up -d`
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
});
await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId });
} catch (error) {

View File

@@ -26,8 +26,10 @@ export default async function (data) {
throw 'No Services found in docker-compose file.';
}
let envs = [];
let buildEnvs = [];
if (secrets.length > 0) {
envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, null)];
buildEnvs = [...buildEnvs, ...generateSecrets(secrets, pullmergeRequestId, true, null, true)];
}
const composeVolumes = [];
@@ -43,8 +45,34 @@ export default async function (data) {
let networks = {};
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
value['container_name'] = `${applicationId}-${key}`;
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment']
let environment = typeof value['environment'] === 'undefined' ? [] : value['environment'];
if (Object.keys(environment).length > 0) {
environment = Object.entries(environment).map(([key, value]) => `${key}=${value}`);
}
value['environment'] = [...environment, ...envs];
let build = typeof value['build'] === 'undefined' ? [] : value['build'];
if (typeof build === 'string') {
build = { context: build };
}
const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args'];
let finalArgs = [...buildEnvs];
if (Object.keys(buildArgs).length > 0) {
for (const arg of buildArgs) {
const [key, _] = arg.split('=');
if (finalArgs.filter((env) => env.startsWith(key)).length === 0) {
finalArgs.push(arg);
}
}
}
if (build.length > 0 || buildArgs.length > 0 ) {
value['build'] = {
...build,
args: finalArgs
};
}
value['labels'] = labels;
// TODO: If we support separated volume for each service, we need to add it here
if (value['volumes']?.length > 0) {
@@ -95,7 +123,7 @@ export default async function (data) {
buildId,
applicationId,
dockerId,
command: `docker compose --project-directory ${workdir} pull`
command: `docker compose --project-directory ${workdir} -f ${fileYaml} pull`
});
await saveBuildLog({ line: 'Pulling images from Compose file...', buildId, applicationId });
await executeCommand({
@@ -103,7 +131,7 @@ export default async function (data) {
buildId,
applicationId,
dockerId,
command: `docker compose --project-directory ${workdir} build --progress plain`
command: `docker compose --project-directory ${workdir} -f ${fileYaml} build --progress plain`
});
await saveBuildLog({ line: 'Building images from Compose file...', buildId, applicationId });
}

View File

@@ -3,7 +3,7 @@ import { generateSecrets } from '../common';
import { buildCacheImageForLaravel, buildImage } from './common';
const createDockerfile = async (data, image): Promise<void> => {
const { workdir, applicationId, tag, buildId, port } = data;
const { workdir, applicationId, tag, buildId, port, secrets, pullmergeRequestId } = data;
const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`);

View File

@@ -19,7 +19,7 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common
import { scheduler } from './scheduler';
import type { ExecaChildProcess } from 'execa';
export const version = '3.12.9';
export const version = '3.12.18';
export const isDev = process.env.NODE_ENV === 'development';
export const sentryDSN =
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
@@ -714,8 +714,10 @@ export async function startTraefikProxy(id: string): Promise<void> {
--network coolify-infra \
-p "80:80" \
-p "443:443" \
${isDev ? '-p "8080:8080"' : ''} \
--name coolify-proxy \
-d ${defaultTraefikImage} \
${isDev ? '--api.insecure=true' : ''} \
--entrypoints.web.address=:80 \
--entrypoints.web.forwardedHeaders.insecure=true \
--entrypoints.websecure.address=:443 \
@@ -1712,78 +1714,24 @@ export function convertTolOldVolumeNames(type) {
}
}
export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
// Cleanup old coolify images
export async function cleanupDockerStorage(dockerId) {
// Cleanup images that are not used by any container
try {
let { stdout: images } = await executeCommand({
dockerId,
command: `docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs -r`,
shell: true
});
images = images.trim();
if (images) {
await executeCommand({
dockerId,
command: `docker rmi -f ${images}" -q | xargs -r`,
shell: true
});
}
await executeCommand({ dockerId, command: `docker image prune -af` });
} catch (error) {}
if (lowDiskSpace || force) {
// Cleanup images that are not used
try {
await executeCommand({ dockerId, command: `docker image prune -f` });
} catch (error) {}
const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({
where: { id: '0' }
});
const { stdout: images } = await executeCommand({
// Prune coolify managed containers
try {
await executeCommand({
dockerId,
command: `docker images|grep -v "<none>"|grep -v REPOSITORY|awk '{print $1, $2}'`,
shell: true
command: `docker container prune -f --filter "label=coolify.managed=true"`
});
const imagesArray = images.trim().replaceAll(' ', ':').split('\n');
const imagesSet = new Set(imagesArray.map((image) => image.split(':')[0]));
let deleteImage = [];
for (const image of imagesSet) {
let keepImage = [];
for (const image2 of imagesArray) {
if (image2.startsWith(image)) {
if (force) {
deleteImage.push(image2);
continue;
}
if (keepImage.length >= numberOfDockerImagesKeptLocally) {
deleteImage.push(image2);
} else {
keepImage.push(image2);
}
}
}
}
for (const image of deleteImage) {
try {
await executeCommand({ dockerId, command: `docker image rm -f ${image}` });
} catch (error) {
console.log(error);
}
}
} catch (error) {}
// Prune coolify managed containers
try {
await executeCommand({
dockerId,
command: `docker container prune -f --filter "label=coolify.managed=true"`
});
} catch (error) {}
// Cleanup build caches
try {
await executeCommand({ dockerId, command: `docker builder prune -a -f` });
} catch (error) {}
}
// Cleanup build caches
try {
await executeCommand({ dockerId, command: `docker builder prune -af` });
} catch (error) {}
}
export function persistentVolumes(id, persistentStorage, config) {
@@ -1912,7 +1860,8 @@ export function generateSecrets(
secrets: Array<any>,
pullmergeRequestId: string,
isBuild = false,
port = null
port = null,
compose = false
): Array<string> {
const envs = [];
const isPRMRSecret = secrets.filter((s) => s.isPRMRSecret);
@@ -1923,7 +1872,7 @@ export function generateSecrets(
return;
}
const build = isBuild && secret.isBuildSecret;
envs.push(parseSecret(secret, build));
envs.push(parseSecret(secret, compose ? false : build));
});
}
if (!pullmergeRequestId && normalSecrets.length > 0) {
@@ -1932,7 +1881,7 @@ export function generateSecrets(
return;
}
const build = isBuild && secret.isBuildSecret;
envs.push(parseSecret(secret, build));
envs.push(parseSecret(secret, compose ? false : build));
});
}
const portFound = envs.filter((env) => env.startsWith('PORT'));

View File

@@ -122,6 +122,9 @@ export async function cleanupUnconfiguredApplications(request: FastifyRequest<an
include: { settings: true, destinationDocker: true, teams: true }
});
for (const application of applications) {
if (application?.buildPack === 'compose') {
continue;
}
if (
!application.buildPack ||
!application.destinationDockerId ||
@@ -500,14 +503,24 @@ export async function saveApplicationSettings(
projectId,
isBot,
isDBBranching,
isCustomSSL
isCustomSSL,
isHttp2
} = request.body;
await prisma.application.update({
where: { id },
data: {
fqdn: isBot ? null : undefined,
settings: {
update: { debug, previews, dualCerts, autodeploy, isBot, isDBBranching, isCustomSSL }
update: {
debug,
previews,
dualCerts,
autodeploy,
isBot,
isDBBranching,
isCustomSSL,
isHttp2
}
}
},
include: { destinationDocker: true }
@@ -670,7 +683,7 @@ export async function restartApplication(
await executeCommand({
dockerId,
command: `docker compose --project-directory ${workdir} up -d`
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
});
return reply.code(201).send();
}
@@ -719,14 +732,15 @@ export async function deleteApplication(
) {
try {
const { id } = request.params;
const { force } = request.body;
const { teamId } = request.user;
const application = await prisma.application.findUnique({
where: { id },
include: { destinationDocker: true }
include: { destinationDocker: true, teams: true }
});
if (!force && application?.destinationDockerId && application.destinationDocker?.network) {
if (!application.teams.find((team) => team.id === teamId) || teamId !== '0') {
throw { status: 403, message: 'You are not allowed to delete this application.' };
}
if (application?.destinationDocker?.id && application.destinationDocker?.network) {
const { stdout: containers } = await executeCommand({
dockerId: application.destinationDocker.id,
command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'`
@@ -746,6 +760,7 @@ export async function deleteApplication(
await prisma.secret.deleteMany({ where: { applicationId: id } });
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: id } });
await prisma.previewApplication.deleteMany({ where: { applicationId: id } });
if (teamId === '0') {
await prisma.application.deleteMany({ where: { id } });
} else {
@@ -1451,7 +1466,7 @@ export async function restartPreview(
await executeCommand({ dockerId, command: `docker rm ${id}-${pullmergeRequestId}` });
await executeCommand({
dockerId,
command: `docker compose --project-directory ${workdir} up -d`
command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d`
});
return reply.code(201).send();
}
@@ -1605,12 +1620,7 @@ export async function getApplicationLogs(request: FastifyRequest<GetApplicationL
.split('\n')
.map((l) => ansi(l))
.filter((a) => a);
const logs = stripLogsStderr.concat(stripLogsStdout);
const sortedLogs = logs.sort((a, b) =>
day(a.split(' ')[0]).isAfter(day(b.split(' ')[0])) ? 1 : -1
);
return { logs: sortedLogs };
// }
return { logs: stripLogsStderr.concat(stripLogsStdout) };
} catch (error) {
const { statusCode, stderr } = error;
if (stderr.startsWith('Error: No such container')) {

View File

@@ -1,154 +1,170 @@
import type { OnlyId } from "../../../../types";
import type { OnlyId } from '../../../../types';
export interface SaveApplication extends OnlyId {
Body: {
name: string,
buildPack: string,
fqdn: string,
port: number,
exposePort: number,
installCommand: string,
buildCommand: string,
startCommand: string,
baseDirectory: string,
publishDirectory: string,
pythonWSGI: string,
pythonModule: string,
pythonVariable: string,
dockerFileLocation: string,
denoMainFile: string,
denoOptions: string,
baseImage: string,
gitCommitHash: string,
baseBuildImage: string,
deploymentType: string,
baseDatabaseBranch: string,
dockerComposeFile: string,
dockerComposeFileLocation: string,
dockerComposeConfiguration: string,
simpleDockerfile: string,
dockerRegistryImageName: string
}
Body: {
name: string;
buildPack: string;
fqdn: string;
port: number;
exposePort: number;
installCommand: string;
buildCommand: string;
startCommand: string;
baseDirectory: string;
publishDirectory: string;
pythonWSGI: string;
pythonModule: string;
pythonVariable: string;
dockerFileLocation: string;
denoMainFile: string;
denoOptions: string;
baseImage: string;
gitCommitHash: string;
baseBuildImage: string;
deploymentType: string;
baseDatabaseBranch: string;
dockerComposeFile: string;
dockerComposeFileLocation: string;
dockerComposeConfiguration: string;
simpleDockerfile: string;
dockerRegistryImageName: string;
};
}
export interface SaveApplicationSettings extends OnlyId {
Querystring: { domain: string; };
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; isDBBranching: boolean, isCustomSSL: boolean };
Querystring: { domain: string };
Body: {
debug: boolean;
previews: boolean;
dualCerts: boolean;
autodeploy: boolean;
branch: string;
projectId: number;
isBot: boolean;
isDBBranching: boolean;
isCustomSSL: boolean;
isHttp2: boolean;
};
}
export interface DeleteApplication extends OnlyId {
Querystring: { domain: string; };
Body: { force: boolean }
Querystring: { domain: string };
Body: { force: boolean };
}
export interface CheckDomain extends OnlyId {
Querystring: { domain: string; };
Querystring: { domain: string };
}
export interface CheckDNS extends OnlyId {
Querystring: { domain: string; };
Body: {
exposePort: number,
fqdn: string,
forceSave: boolean,
dualCerts: boolean
}
Querystring: { domain: string };
Body: {
exposePort: number;
fqdn: string;
forceSave: boolean;
dualCerts: boolean;
};
}
export interface DeployApplication {
Querystring: { domain: string }
Body: { pullmergeRequestId: string | null, branch: string, forceRebuild?: boolean }
Querystring: { domain: string };
Body: { pullmergeRequestId: string | null; branch: string; forceRebuild?: boolean };
}
export interface GetImages {
Body: { buildPack: string, deploymentType: string }
Body: { buildPack: string; deploymentType: string };
}
export interface SaveApplicationSource extends OnlyId {
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string, simpleDockerfile?: string }
Body: {
gitSourceId?: string | null;
forPublic?: boolean;
type?: string;
simpleDockerfile?: string;
};
}
export interface CheckRepository extends OnlyId {
Querystring: { repository: string, branch: string }
Querystring: { repository: string; branch: string };
}
export interface SaveDestination extends OnlyId {
Body: { destinationId: string }
Body: { destinationId: string };
}
export interface SaveSecret extends OnlyId {
Body: {
name: string,
value: string,
isBuildSecret: boolean,
previewSecret: boolean,
isNew: boolean
}
Body: {
name: string;
value: string;
isBuildSecret: boolean;
previewSecret: boolean;
isNew: boolean;
};
}
export interface DeleteSecret extends OnlyId {
Body: { name: string }
Body: { name: string };
}
export interface SaveStorage extends OnlyId {
Body: {
path: string,
newStorage: boolean,
storageId: string
}
Body: {
path: string;
newStorage: boolean;
storageId: string;
};
}
export interface DeleteStorage extends OnlyId {
Body: {
path: string,
}
Body: {
path: string;
};
}
export interface GetApplicationLogs {
Params: {
id: string,
containerId: string
}
Querystring: {
since: number,
}
Params: {
id: string;
containerId: string;
};
Querystring: {
since: number;
};
}
export interface GetBuilds extends OnlyId {
Querystring: {
buildId: string
skip: number,
}
Querystring: {
buildId: string;
skip: number;
};
}
export interface GetBuildIdLogs {
Params: {
id: string,
buildId: string
},
Querystring: {
sequence: number
}
Params: {
id: string;
buildId: string;
};
Querystring: {
sequence: number;
};
}
export interface SaveDeployKey extends OnlyId {
Body: {
deployKeyId: number
}
Body: {
deployKeyId: number;
};
}
export interface CancelDeployment {
Body: {
buildId: string,
applicationId: string
}
Body: {
buildId: string;
applicationId: string;
};
}
export interface DeployApplication extends OnlyId {
Body: {
pullmergeRequestId: string | null,
branch: string,
forceRebuild?: boolean
}
Body: {
pullmergeRequestId: string | null;
branch: string;
forceRebuild?: boolean;
};
}
export interface StopPreviewApplication extends OnlyId {
Body: {
pullmergeRequestId: string | null,
}
Body: {
pullmergeRequestId: string | null;
};
}
export interface RestartPreviewApplication {
Params: {
id: string,
pullmergeRequestId: string | null,
}
Params: {
id: string;
pullmergeRequestId: string | null;
};
}
export interface RestartApplication {
Params: {
id: string,
},
Body: {
imageId: string | null,
}
}
Params: {
id: string;
};
Body: {
imageId: string | null;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ export interface SaveDatabaseType extends OnlyId {
Body: { type: string }
}
export interface DeleteDatabase extends OnlyId {
Body: { force: string }
Body: { }
}
export interface SaveVersion extends OnlyId {
Body: {

View File

@@ -1,6 +1,6 @@
import { compareVersions } from "compare-versions";
import cuid from "cuid";
import bcrypt from "bcryptjs";
import { compareVersions } from 'compare-versions';
import cuid from 'cuid';
import bcrypt from 'bcryptjs';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import {
@@ -13,12 +13,12 @@ import {
uniqueName,
version,
sentryDSN,
executeCommand,
} from "../../../lib/common";
import { scheduler } from "../../../lib/scheduler";
import type { FastifyReply, FastifyRequest } from "fastify";
import type { Login, Update } from ".";
import type { GetCurrentUser } from "./types";
executeCommand
} from '../../../lib/common';
import { scheduler } from '../../../lib/scheduler';
import type { FastifyReply, FastifyRequest } from 'fastify';
import type { Login, Update } from '.';
import type { GetCurrentUser } from './types';
export async function hashPassword(password: string): Promise<string> {
const saltRounds = 15;
@@ -29,9 +29,9 @@ export async function backup(request: FastifyRequest) {
try {
const { backupData } = request.params;
let std = null;
const [id, backupType, type, zipped, storage] = backupData.split(':')
console.log(id, backupType, type, zipped, storage)
const database = await prisma.database.findUnique({ where: { id } })
const [id, backupType, type, zipped, storage] = backupData.split(':');
console.log(id, backupType, type, zipped, storage);
const database = await prisma.database.findUnique({ where: { id } });
if (database) {
// await executeDockerCmd({
// dockerId: database.destinationDockerId,
@@ -40,8 +40,7 @@ export async function backup(request: FastifyRequest) {
std = await executeCommand({
dockerId: database.destinationDockerId,
command: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v coolify-local-backup:/app/backups -e CONTAINERS_TO_BACKUP="${backupData}" coollabsio/backup`
})
});
}
if (std.stdout) {
return std.stdout;
@@ -58,9 +57,9 @@ export async function cleanupManually(request: FastifyRequest) {
try {
const { serverId } = request.body;
const destination = await prisma.destinationDocker.findUnique({
where: { id: serverId },
where: { id: serverId }
});
await cleanupDockerStorage(destination.id, true, true);
await cleanupDockerStorage(destination.id);
return {};
} catch ({ status, message }) {
return errorHandler({ status, message });
@@ -68,17 +67,25 @@ export async function cleanupManually(request: FastifyRequest) {
}
export async function refreshTags() {
try {
const { default: got } = await import('got')
const { default: got } = await import('got');
try {
if (isDev) {
const tags = await fs.readFile('./devTags.json', 'utf8')
await fs.writeFile('./tags.json', tags)
let tags = await fs.readFile('./devTags.json', 'utf8');
try {
if (await fs.stat('./testTags.json')) {
const testTags = await fs.readFile('./testTags.json', 'utf8');
if (testTags.length > 0) {
tags = JSON.parse(tags).concat(JSON.parse(testTags));
}
}
} catch (error) {}
await fs.writeFile('./tags.json', tags);
} else {
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text()
await fs.writeFile('/app/tags.json', tags)
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
await fs.writeFile('/app/tags.json', tags);
}
} catch (error) {
console.log(error)
console.log(error);
}
return {};
@@ -88,17 +95,25 @@ export async function refreshTags() {
}
export async function refreshTemplates() {
try {
const { default: got } = await import('got')
const { default: got } = await import('got');
try {
if (isDev) {
const response = await fs.readFile('./devTemplates.yaml', 'utf8')
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response)))
let templates = await fs.readFile('./devTemplates.yaml', 'utf8');
try {
if (await fs.stat('./testTemplate.yaml')) {
templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8'));
}
} catch (error) {}
const response = await fs.readFile('./devTemplates.yaml', 'utf8');
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response)));
} else {
const response = await got.get('https://get.coollabs.io/coolify/service-templates.yaml').text()
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)))
const response = await got
.get('https://get.coollabs.io/coolify/service-templates.yaml')
.text();
await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response)));
}
} catch (error) {
console.log(error)
console.log(error);
}
return {};
} catch ({ status, message }) {
@@ -107,28 +122,29 @@ export async function refreshTemplates() {
}
export async function checkUpdate(request: FastifyRequest) {
try {
const { default: got } = await import('got')
const { default: got } = await import('got');
const isStaging =
request.hostname === "staging.coolify.io" ||
request.hostname === "arm.coolify.io";
request.hostname === 'staging.coolify.io' || request.hostname === 'arm.coolify.io';
const currentVersion = version;
const { coolify } = await got.get('https://get.coollabs.io/versions.json', {
searchParams: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
}).json()
const { coolify } = await got
.get('https://get.coollabs.io/versions.json', {
searchParams: {
appId: process.env['COOLIFY_APP_ID'] || undefined,
version: currentVersion
}
})
.json();
const latestVersion = coolify.main.version;
const isUpdateAvailable = compareVersions(latestVersion, currentVersion);
if (isStaging) {
return {
isUpdateAvailable: true,
latestVersion: "next",
latestVersion: 'next'
};
}
return {
isUpdateAvailable: isStaging ? true : isUpdateAvailable === 1,
latestVersion,
latestVersion
};
} catch ({ status, message }) {
return errorHandler({ status, message });
@@ -142,8 +158,13 @@ export async function update(request: FastifyRequest<Update>) {
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
await executeCommand({ command: `docker pull coollabsio/coolify:${latestVersion}` });
await executeCommand({ shell: true, command: `env | grep COOLIFY > .env` });
await executeCommand({ command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` });
await executeCommand({ shell: true, command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"` });
await executeCommand({
command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
});
await executeCommand({
shell: true,
command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"`
});
return {};
} else {
await asyncSleep(2000);
@@ -156,12 +177,12 @@ export async function update(request: FastifyRequest<Update>) {
export async function resetQueue(request: FastifyRequest<any>) {
try {
const teamId = request.user.teamId;
if (teamId === "0") {
if (teamId === '0') {
await prisma.build.updateMany({
where: { status: { in: ["queued", "running"] } },
data: { status: "canceled" },
where: { status: { in: ['queued', 'running'] } },
data: { status: 'canceled' }
});
scheduler.workers.get("deployApplication").postMessage("cancel");
scheduler.workers.get('deployApplication').postMessage('cancel');
}
} catch ({ status, message }) {
return errorHandler({ status, message });
@@ -170,7 +191,7 @@ export async function resetQueue(request: FastifyRequest<any>) {
export async function restartCoolify(request: FastifyRequest<any>) {
try {
const teamId = request.user.teamId;
if (teamId === "0") {
if (teamId === '0') {
if (!isDev) {
await executeCommand({ command: `docker restart coolify` });
return {};
@@ -180,7 +201,7 @@ export async function restartCoolify(request: FastifyRequest<any>) {
}
throw {
status: 500,
message: "You are not authorized to restart Coolify.",
message: 'You are not authorized to restart Coolify.'
};
} catch ({ status, message }) {
return errorHandler({ status, message });
@@ -192,43 +213,52 @@ export async function showDashboard(request: FastifyRequest) {
const userId = request.user.userId;
const teamId = request.user.teamId;
let applications = await prisma.application.findMany({
where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true }
});
const databases = await prisma.database.findMany({
where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true }
});
const services = await prisma.service.findMany({
where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } },
include: { destinationDocker: true, teams: true },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, teams: true }
});
const gitSources = await prisma.gitSource.findMany({
where: { OR: [{ teams: { some: { id: teamId === "0" ? undefined : teamId } } }, { isSystemWide: true }] },
include: { teams: true },
where: {
OR: [
{ teams: { some: { id: teamId === '0' ? undefined : teamId } } },
{ isSystemWide: true }
]
},
include: { teams: true }
});
const destinations = await prisma.destinationDocker.findMany({
where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } },
include: { teams: true },
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { teams: true }
});
const settings = await listSettings();
let foundUnconfiguredApplication = false;
for (const application of applications) {
if (((!application.buildPack || !application.branch) && !application.simpleDockerfile) || !application.destinationDockerId || (!application.settings?.isBot && !application?.fqdn) && application.buildPack !== "compose") {
foundUnconfiguredApplication = true
if (
((!application.buildPack || !application.branch) && !application.simpleDockerfile) ||
!application.destinationDockerId ||
(!application.settings?.isBot && !application?.fqdn && application.buildPack !== 'compose')
) {
foundUnconfiguredApplication = true;
}
}
let foundUnconfiguredService = false;
for (const service of services) {
if (!service.fqdn) {
foundUnconfiguredService = true
foundUnconfiguredService = true;
}
}
let foundUnconfiguredDatabase = false;
for (const database of databases) {
if (!database.version) {
foundUnconfiguredDatabase = true
foundUnconfiguredDatabase = true;
}
}
return {
@@ -240,101 +270,94 @@ export async function showDashboard(request: FastifyRequest) {
services,
gitSources,
destinations,
settings,
settings
};
} catch ({ status, message }) {
return errorHandler({ status, message });
}
}
export async function login(
request: FastifyRequest<Login>,
reply: FastifyReply
) {
export async function login(request: FastifyRequest<Login>, reply: FastifyReply) {
if (request.user) {
return reply.redirect("/dashboard");
return reply.redirect('/dashboard');
} else {
const { email, password, isLogin } = request.body || {};
if (!email || !password) {
throw { status: 500, message: "Email and password are required." };
throw { status: 500, message: 'Email and password are required.' };
}
const users = await prisma.user.count();
const userFound = await prisma.user.findUnique({
where: { email },
include: { teams: true, permission: true },
rejectOnNotFound: false,
rejectOnNotFound: false
});
if (!userFound && isLogin) {
throw { status: 500, message: "User not found." };
throw { status: 500, message: 'User not found.' };
}
const { isRegistrationEnabled, id } = await prisma.setting.findFirst();
let uid = cuid();
let permission = "read";
let permission = 'read';
let isAdmin = false;
if (users === 0) {
await prisma.setting.update({
where: { id },
data: { isRegistrationEnabled: false },
data: { isRegistrationEnabled: false }
});
uid = "0";
uid = '0';
}
if (userFound) {
if (userFound.type === "email") {
if (userFound.password === "RESETME") {
if (userFound.type === 'email') {
if (userFound.password === 'RESETME') {
const hashedPassword = await hashPassword(password);
if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) {
if (userFound.id === "0") {
if (userFound.id === '0') {
await prisma.user.update({
where: { email: userFound.email },
data: { password: "RESETME" },
data: { password: 'RESETME' }
});
} else {
await prisma.user.update({
where: { email: userFound.email },
data: { password: "RESETTIMEOUT" },
data: { password: 'RESETTIMEOUT' }
});
}
throw {
status: 500,
message:
"Password reset link has expired. Please request a new one.",
message: 'Password reset link has expired. Please request a new one.'
};
} else {
await prisma.user.update({
where: { email: userFound.email },
data: { password: hashedPassword },
data: { password: hashedPassword }
});
return {
userId: userFound.id,
teamId: userFound.id,
permission: userFound.permission,
isAdmin: true,
isAdmin: true
};
}
}
const passwordMatch = await bcrypt.compare(
password,
userFound.password
);
const passwordMatch = await bcrypt.compare(password, userFound.password);
if (!passwordMatch) {
throw {
status: 500,
message: "Wrong password or email address.",
message: 'Wrong password or email address.'
};
}
uid = userFound.id;
isAdmin = true;
}
} else {
permission = "owner";
permission = 'owner';
isAdmin = true;
if (!isRegistrationEnabled) {
throw {
status: 404,
message: "Registration disabled by administrator.",
message: 'Registration disabled by administrator.'
};
}
const hashedPassword = await hashPassword(password);
@@ -344,17 +367,17 @@ export async function login(
id: uid,
email,
password: hashedPassword,
type: "email",
type: 'email',
teams: {
create: {
id: uid,
name: uniqueName(),
destinationDocker: { connect: { network: "coolify" } },
},
destinationDocker: { connect: { network: 'coolify' } }
}
},
permission: { create: { teamId: uid, permission: "owner" } },
permission: { create: { teamId: uid, permission: 'owner' } }
},
include: { teams: true },
include: { teams: true }
});
} else {
await prisma.user.create({
@@ -362,16 +385,16 @@ export async function login(
id: uid,
email,
password: hashedPassword,
type: "email",
type: 'email',
teams: {
create: {
id: uid,
name: uniqueName(),
},
name: uniqueName()
}
},
permission: { create: { teamId: uid, permission: "owner" } },
permission: { create: { teamId: uid, permission: 'owner' } }
},
include: { teams: true },
include: { teams: true }
});
}
}
@@ -379,23 +402,20 @@ export async function login(
userId: uid,
teamId: uid,
permission,
isAdmin,
isAdmin
};
}
}
export async function getCurrentUser(
request: FastifyRequest<GetCurrentUser>,
fastify
) {
export async function getCurrentUser(request: FastifyRequest<GetCurrentUser>, fastify) {
let token = null;
const { teamId } = request.query;
try {
const user = await prisma.user.findUnique({
where: { id: request.user.userId },
where: { id: request.user.userId }
});
if (!user) {
throw "User not found";
throw 'User not found';
}
} catch (error) {
throw { status: 401, message: error };
@@ -404,17 +424,15 @@ export async function getCurrentUser(
try {
const user = await prisma.user.findFirst({
where: { id: request.user.userId, teams: { some: { id: teamId } } },
include: { teams: true, permission: true },
include: { teams: true, permission: true }
});
if (user) {
const permission = user.permission.find(
(p) => p.teamId === teamId
).permission;
const permission = user.permission.find((p) => p.teamId === teamId).permission;
const payload = {
...request.user,
teamId,
permission: permission || null,
isAdmin: permission === "owner" || permission === "admin",
isAdmin: permission === 'owner' || permission === 'admin'
};
token = fastify.jwt.sign(payload);
}
@@ -422,12 +440,14 @@ export async function getCurrentUser(
// No new token -> not switching teams
}
}
const pendingInvitations = await prisma.teamInvitation.findMany({ where: { uid: request.user.userId } })
const pendingInvitations = await prisma.teamInvitation.findMany({
where: { uid: request.user.userId }
});
return {
settings: await prisma.setting.findUnique({ where: { id: "0" } }),
settings: await prisma.setting.findUnique({ where: { id: '0' } }),
sentryDSN,
pendingInvitations,
token,
...request.user,
...request.user
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,32 @@
import { FastifyRequest } from "fastify";
import { errorHandler, getDomain, isDev, prisma, executeCommand } from "../../../lib/common";
import { getTemplates } from "../../../lib/services";
import { OnlyId } from "../../../types";
import { FastifyRequest } from 'fastify';
import { errorHandler, getDomain, isDev, prisma, executeCommand } from '../../../lib/common';
import { getTemplates } from '../../../lib/services';
import { OnlyId } from '../../../types';
import { parseAndFindServiceTemplates } from '../../api/v1/services/handlers';
function generateServices(serviceId, containerId, port) {
function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps = false) {
if (isHttp2) {
return {
[serviceId]: {
loadbalancer: {
servers: [
{
url: `${isHttps ? 'https' : 'http'}://${containerId}:${port}`
}
]
}
},
[`${serviceId}-http2`]: {
loadbalancer: {
servers: [
{
url: `h2c://${containerId}:${port}`
}
]
}
}
};
}
return {
[serviceId]: {
loadbalancer: {
@@ -14,43 +37,57 @@ function generateServices(serviceId, containerId, port) {
]
}
}
}
};
}
function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, isDualCerts, isCustomSSL) {
function generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
isDualCerts,
isCustomSSL,
isHttp2 = false
) {
let rule = `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`;
let ruleWWW = `Host(\`www.${nakedDomain}\`)${
pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''
}`;
let http: any = {
entrypoints: ['web'],
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
rule,
service: `${serviceId}`,
priority: 2,
middlewares: []
}
};
let https: any = {
entrypoints: ['websecure'],
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
rule,
service: `${serviceId}`,
priority: 2,
tls: {
certresolver: 'letsencrypt'
},
middlewares: []
}
};
let httpWWW: any = {
entrypoints: ['web'],
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
rule: ruleWWW,
service: `${serviceId}`,
priority: 2,
middlewares: []
}
};
let httpsWWW: any = {
entrypoints: ['websecure'],
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
rule: ruleWWW,
service: `${serviceId}`,
priority: 2,
tls: {
certresolver: 'letsencrypt'
},
middlewares: []
}
};
// 2. http + non-www only
if (!isHttps && !isWWW) {
https.middlewares.push('redirect-to-http');
@@ -58,19 +95,19 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
httpWWW.middlewares.push('redirect-to-non-www');
httpsWWW.middlewares.push('redirect-to-non-www');
delete https.tls
delete httpsWWW.tls
delete https.tls;
delete httpsWWW.tls;
}
// 3. http + www only
// 3. http + www only
if (!isHttps && isWWW) {
https.middlewares.push('redirect-to-http');
httpsWWW.middlewares.push('redirect-to-http');
http.middlewares.push('redirect-to-www');
https.middlewares.push('redirect-to-www');
delete https.tls
delete httpsWWW.tls
delete https.tls;
delete httpsWWW.tls;
}
// 5. https + non-www only
if (isHttps && !isWWW) {
@@ -86,17 +123,17 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
httpsWWW.tls = true;
} else {
https.tls = true;
delete httpsWWW.tls.certresolver
delete httpsWWW.tls.certresolver;
httpsWWW.tls.domains = {
main: domain
}
};
}
} else {
if (!isDualCerts) {
delete httpsWWW.tls.certresolver
delete httpsWWW.tls.certresolver;
httpsWWW.tls.domains = {
main: domain
}
};
}
}
}
@@ -114,26 +151,59 @@ function generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, is
httpsWWW.tls = true;
} else {
httpsWWW.tls = true;
delete https.tls.certresolver
delete https.tls.certresolver;
https.tls.domains = {
main: domain
}
};
}
} else {
if (!isDualCerts) {
delete https.tls.certresolver
delete https.tls.certresolver;
https.tls.domains = {
main: domain
}
};
}
}
}
if (isHttp2) {
let http2 = {
...http,
service: `${serviceId}-http2`,
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
};
let http2WWW = {
...httpWWW,
service: `${serviceId}-http2`,
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
};
let https2 = {
...https,
service: `${serviceId}-http2`,
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
};
let https2WWW = {
...httpsWWW,
service: `${serviceId}-http2`,
rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)`
};
return {
[`${serviceId}-${pathPrefix}`]: { ...http },
[`${serviceId}-${pathPrefix}-http2`]: { ...http2 },
[`${serviceId}-${pathPrefix}-secure`]: { ...https },
[`${serviceId}-${pathPrefix}-secure-http2`]: { ...https2 },
[`${serviceId}-${pathPrefix}-www`]: { ...httpWWW },
[`${serviceId}-${pathPrefix}-www-http2`]: { ...http2WWW },
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW },
[`${serviceId}-${pathPrefix}-secure-www-http2`]: { ...https2WWW }
};
}
return {
[`${serviceId}-${pathPrefix}`]: { ...http },
[`${serviceId}-${pathPrefix}-secure`]: { ...https },
[`${serviceId}-${pathPrefix}-www`]: { ...httpWWW },
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW },
}
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW }
};
}
export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote: boolean = false) {
const traefik = {
@@ -174,26 +244,26 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const coolifySettings = await prisma.setting.findFirst();
if (coolifySettings.isTraefikUsed && coolifySettings.proxyDefaultRedirect) {
traefik.http.routers['catchall-http'] = {
entrypoints: ["web"],
rule: "HostRegexp(`{catchall:.*}`)",
service: "noop",
entrypoints: ['web'],
rule: 'HostRegexp(`{catchall:.*}`)',
service: 'noop',
priority: 1,
middlewares: ["redirect-regexp"]
}
middlewares: ['redirect-regexp']
};
traefik.http.routers['catchall-https'] = {
entrypoints: ["websecure"],
rule: "HostRegexp(`{catchall:.*}`)",
service: "noop",
entrypoints: ['websecure'],
rule: 'HostRegexp(`{catchall:.*}`)',
service: 'noop',
priority: 1,
middlewares: ["redirect-regexp"]
}
middlewares: ['redirect-regexp']
};
traefik.http.middlewares['redirect-regexp'] = {
redirectregex: {
regex: '(.*)',
replacement: coolifySettings.proxyDefaultRedirect,
permanent: false
}
}
};
traefik.http.services['noop'] = {
loadBalancer: {
servers: [
@@ -202,25 +272,41 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
}
]
}
}
};
}
const sslpath = '/etc/traefik/acme/custom';
let certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } } } } })
let certificates = await prisma.certificate.findMany({
where: {
team: {
applications: { some: { settings: { isCustomSSL: true } } },
destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } }
}
}
});
if (remote) {
certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { id, remoteEngine: true, isCoolifyProxyUsed: true, remoteVerified: true } } } } })
certificates = await prisma.certificate.findMany({
where: {
team: {
applications: { some: { settings: { isCustomSSL: true } } },
destinationDocker: {
some: { id, remoteEngine: true, isCoolifyProxyUsed: true, remoteVerified: true }
}
}
}
});
}
let parsedCertificates = []
let parsedCertificates = [];
for (const certificate of certificates) {
parsedCertificates.push({
certFile: `${sslpath}/${certificate.id}-cert.pem`,
keyFile: `${sslpath}/${certificate.id}-key.pem`
})
});
}
if (parsedCertificates.length > 0) {
traefik.tls.certificates = parsedCertificates
traefik.tls.certificates = parsedCertificates;
}
let applications = [];
@@ -236,7 +322,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
destinationDocker: true,
persistentStorage: true,
serviceSecret: true,
serviceSetting: true,
serviceSetting: true
},
orderBy: { createdAt: 'desc' }
});
@@ -251,23 +337,25 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
destinationDocker: true,
persistentStorage: true,
serviceSecret: true,
serviceSetting: true,
serviceSetting: true
},
orderBy: { createdAt: 'desc' },
orderBy: { createdAt: 'desc' }
});
}
if (applications.length > 0) {
const dockerIds = new Set()
const runningContainers = {}
const dockerIds = new Set();
const runningContainers = {};
applications.forEach((app) => dockerIds.add(app.destinationDocker.id));
for (const dockerId of dockerIds) {
const { stdout: container } = await executeCommand({ dockerId, command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` })
const { stdout: container } = await executeCommand({
dockerId,
command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'`
});
if (container) {
const containersArray = container.trim().split('\n');
if (containersArray.length > 0) {
runningContainers[dockerId] = containersArray
runningContainers[dockerId] = containersArray;
}
}
}
@@ -289,38 +377,54 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
if (
!runningContainers[destinationDockerId] ||
runningContainers[destinationDockerId].length === 0 ||
runningContainers[destinationDockerId].filter((container) => container.startsWith(id)).length === 0
runningContainers[destinationDockerId].filter((container) => container.startsWith(id))
.length === 0
) {
continue
continue;
}
if (buildPack === 'compose') {
const services = Object.entries(JSON.parse(dockerComposeConfiguration))
const services = Object.entries(JSON.parse(dockerComposeConfiguration));
if (services.length > 0) {
for (const service of services) {
const [key, value] = service
const [key, value] = service;
if (key && value) {
if (!value.fqdn || !value.port) {
continue;
}
const { fqdn, port } = value
const containerId = `${id}-${key}`
const { fqdn, port } = value;
const containerId = `${id}-${key}`;
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const pathPrefix = '/'
const pathPrefix = '/';
const isCustomSSL = false;
const dualCerts = false;
const serviceId = `${id}-${port || 'default'}`
const serviceId = `${id}-${port || 'default'}`;
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, containerId, port) }
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, containerId, port)
};
}
}
}
continue;
}
const { previews, dualCerts, isCustomSSL } = settings;
const { previews, dualCerts, isCustomSSL, isHttp2 } = settings;
const { network, id: dockerId } = destinationDocker;
if (!fqdn) {
continue;
@@ -329,12 +433,31 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const pathPrefix = '/'
const serviceId = `${id}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
const pathPrefix = '/';
const serviceId = `${id}-${port || 'default'}`;
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL,
isHttp2
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, id, port, isHttp2, isHttps)
};
if (previews) {
const { stdout } = await executeCommand({ dockerId, command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` })
const { stdout } = await executeCommand({
dockerId,
command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"`
});
if (stdout) {
const containers = stdout
.trim()
@@ -343,44 +466,57 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
.map((c) => c.replace(/"/g, ''));
if (containers.length > 0) {
for (const container of containers) {
const previewDomain = `${container.split('-')[1]}${coolifySettings.previewSeparator}${domain}`;
const previewDomain = `${container.split('-')[1]}${
coolifySettings.previewSeparator
}${domain}`;
const nakedDomain = previewDomain.replace(/^www\./, '');
const pathPrefix = '/'
const serviceId = `${container}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, previewDomain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
const pathPrefix = '/';
const serviceId = `${container}-${port || 'default'}`;
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
previewDomain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, container, port, isHttp2)
};
}
}
}
}
} catch (error) {
console.log(error)
console.log(error);
}
}
}
if (services.length > 0) {
const dockerIds = new Set()
const runningContainers = {}
const dockerIds = new Set();
const runningContainers = {};
services.forEach((app) => dockerIds.add(app.destinationDocker.id));
for (const dockerId of dockerIds) {
const { stdout: container } = await executeCommand({ dockerId, command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` })
const { stdout: container } = await executeCommand({
dockerId,
command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'`
});
if (container) {
const containersArray = container.trim().split('\n');
if (containersArray.length > 0) {
runningContainers[dockerId] = containersArray
runningContainers[dockerId] = containersArray;
}
}
}
for (const service of services) {
try {
let {
fqdn,
id,
type,
destinationDockerId,
dualCerts,
serviceSetting
} = service;
let { fqdn, id, type, destinationDockerId, dualCerts, serviceSetting } = service;
if (!fqdn) {
continue;
}
@@ -392,7 +528,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
runningContainers[destinationDockerId].length === 0 ||
!runningContainers[destinationDockerId].includes(id)
) {
continue
continue;
}
const templates = await getTemplates();
let found = templates.find((a) => a.type === type);
@@ -401,88 +537,144 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
}
found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id));
for (const oneService of Object.keys(found.services)) {
const isDomainConfiguration = found?.services[oneService]?.proxy?.filter(p => p.domain) ?? [];
if (isDomainConfiguration.length > 0) {
const { proxy } = found.services[oneService];
const isDomainAndProxyConfiguration =
found?.services[oneService]?.proxy?.filter((p) => p.port) ?? [];
if (isDomainAndProxyConfiguration.length > 0) {
const template: any = await parseAndFindServiceTemplates(service, null, true);
const { proxy } = template.services[oneService] || found.services[oneService];
for (let configuration of proxy) {
if (configuration.domain) {
const setting = serviceSetting.find((a) => a.variableName === configuration.domain);
const setting = serviceSetting.find(
(a) => a.variableName === configuration.domain
);
if (setting) {
configuration.domain = configuration.domain.replace(configuration.domain, setting.value);
configuration.domain = configuration.domain.replace(
configuration.domain,
setting.value
);
}
}
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
const foundPortVariable = serviceSetting.find(
(a) => a.name.toLowerCase() === 'port'
);
if (foundPortVariable) {
configuration.port = foundPortVariable.value
configuration.port = foundPortVariable.value;
}
let port, pathPrefix, customDomain;
if (configuration) {
port = configuration?.port;
pathPrefix = configuration?.pathPrefix || '/';
customDomain = configuration?.domain
customDomain = configuration?.domain;
}
if (customDomain) {
fqdn = customDomain
fqdn = customDomain;
} else {
fqdn = service.fqdn
fqdn = service.fqdn;
}
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const isCustomSSL = false;
const serviceId = `${oneService}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, oneService, port) }
const serviceId = `${oneService}-${port || 'default'}`;
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, oneService, port)
};
}
} else {
if (found.services[oneService].ports && found.services[oneService].ports.length > 0) {
for (let [index, port] of found.services[oneService].ports.entries()) {
if (port == 22) continue;
if (index === 0) {
const foundPortVariable = serviceSetting.find((a) => a.name.toLowerCase() === 'port')
const foundPortVariable = serviceSetting.find(
(a) => a.name.toLowerCase() === 'port'
);
if (foundPortVariable) {
port = foundPortVariable.value
port = foundPortVariable.value;
}
}
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const pathPrefix = '/'
const isCustomSSL = false
const serviceId = `${oneService}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, id, port) }
const pathPrefix = '/';
const isCustomSSL = false;
const serviceId = `${oneService}-${port || 'default'}`;
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, id, port)
};
}
}
}
}
} catch (error) {
console.log(error)
console.log(error);
}
}
}
if (!remote) {
const { fqdn, dualCerts } = await prisma.setting.findFirst();
if (!fqdn) {
return
return;
}
const domain = getDomain(fqdn);
const nakedDomain = domain.replace(/^www\./, '');
const isHttps = fqdn.startsWith('https://');
const isWWW = fqdn.includes('www.');
const id = isDev ? 'host.docker.internal' : 'coolify'
const container = isDev ? 'host.docker.internal' : 'coolify'
const port = 3000
const pathPrefix = '/'
const isCustomSSL = false
const serviceId = `${id}-${port || 'default'}`
traefik.http.routers = { ...traefik.http.routers, ...generateRouters(serviceId, domain, nakedDomain, pathPrefix, isHttps, isWWW, dualCerts, isCustomSSL) }
traefik.http.services = { ...traefik.http.services, ...generateServices(serviceId, container, port) }
const id = isDev ? 'host.docker.internal' : 'coolify';
const container = isDev ? 'host.docker.internal' : 'coolify';
const port = 3000;
const pathPrefix = '/';
const isCustomSSL = false;
const serviceId = `${id}-${port || 'default'}`;
traefik.http.routers = {
...traefik.http.routers,
...generateRouters(
serviceId,
domain,
nakedDomain,
pathPrefix,
isHttps,
isWWW,
dualCerts,
isCustomSSL
)
};
traefik.http.services = {
...traefik.http.services,
...generateServices(serviceId, container, port)
};
}
} catch (error) {
console.log(error)
console.log(error);
} finally {
if (Object.keys(traefik.http.routers).length === 0) {
traefik.http.routers = null;
@@ -496,9 +688,9 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
export async function otherProxyConfiguration(request: FastifyRequest<TraefikOtherConfiguration>) {
try {
const { id } = request.query
const { id } = request.query;
if (id) {
const { privatePort, publicPort, type, address = id } = request.query
const { privatePort, publicPort, type, address = id } = request.query;
let traefik = {};
if (publicPort && type && privatePort) {
if (type === 'tcp') {
@@ -559,18 +751,18 @@ export async function otherProxyConfiguration(request: FastifyRequest<TraefikOth
}
}
} else {
throw { status: 500 }
throw { status: 500 };
}
}
} else {
throw { status: 500 }
throw { status: 500 };
}
return {
...traefik
};
}
throw { status: 500 }
throw { status: 500 };
} catch ({ status, message }) {
return errorHandler({ status, message })
return errorHandler({ status, message });
}
}

View File

@@ -4,9 +4,11 @@ import { proxyConfiguration, otherProxyConfiguration } from './handlers';
import { OtherProxyConfiguration } from './types';
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.get<OnlyId>('/main.json', async (request, reply) => proxyConfiguration(request, false));
fastify.get<OnlyId>('/remote/:id', async (request) => proxyConfiguration(request, true));
fastify.get<OtherProxyConfiguration>('/other.json', async (request, reply) => otherProxyConfiguration(request));
fastify.get<OnlyId>('/main.json', async (request, reply) => proxyConfiguration(request, false));
fastify.get<OnlyId>('/remote/:id', async (request) => proxyConfiguration(request, true));
fastify.get<OtherProxyConfiguration>('/other.json', async (request, reply) =>
otherProxyConfiguration(request)
);
};
export default root;

View File

@@ -1,11 +1,9 @@
[
{ "name": "directus-postgresql", "image": "directus/directus", "tags": ["9.22"] },
{ "name": "whoogle", "image": "benbusby/whoogle-search", "tags": ["0.8.1"] },
{ "name": "libretranslate", "image": "libretranslate/libretranslate", "tags": ["v1.3.8"] },
{
"name": "appsmith",
"image": "appsmith/appsmith-ce",
"tags": [
"v1.9.3",
"v1.9.1",
"v1.8.15",
"v1.8.12",
@@ -34,8 +32,7 @@
"v1.6.5",
"v1.6.3",
"v1.6.1",
"v1.5.30",
"v1.5.28"
"v1.5.30"
]
},
{
@@ -74,6 +71,42 @@
"0.3.1"
]
},
{
"name": "directus-postgresql",
"image": "directus/directus",
"tags": [
"9.22.3",
"9.22.0",
"9.21.0",
"9.20.4",
"9.20.2",
"9.20.0",
"9.19.2",
"9.18.0",
"9.17.4",
"9.17.2",
"9.17.0",
"9.16.0",
"9.15.0",
"9.14.5",
"9.14.3",
"9.14.0",
"9.13.0",
"9.12.2",
"9.12.0",
"9.11.0",
"9.10.0",
"9.9.0",
"9.8.0",
"9.7.0",
"9.6.0",
"9.5.2",
"9.5.0",
"9.4.2",
"9.4.0",
"9.3.0"
]
},
{
"name": "fider",
"image": "getfider/fider",
@@ -114,6 +147,9 @@
"name": "ghost-mariadb",
"image": "bitnami/ghost",
"tags": [
"5.30.1",
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -141,9 +177,6 @@
"5.22.4",
"5.22.3",
"5.22.2",
"5.22.1",
"5.22.0",
"5.21.0",
"4.48.8"
]
},
@@ -151,6 +184,8 @@
"name": "ghost-mysql",
"image": "library/ghost",
"tags": [
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -178,15 +213,15 @@
"5.17.2",
"5.17.1",
"5.17.0",
"5.16.2",
"5.14.2",
"5.14.1"
"5.16.2"
]
},
{
"name": "ghost-only",
"image": "library/ghost",
"tags": [
"5.30.0",
"5.29.0",
"5.28.0",
"5.27.0",
"5.26.4",
@@ -214,9 +249,7 @@
"5.17.2",
"5.17.1",
"5.17.0",
"5.16.2",
"5.14.2",
"5.14.1"
"5.16.2"
]
},
{
@@ -373,6 +406,7 @@
"7.0.0",
"6.0.1",
"6.0.0",
"20.0.3",
"20.0.2",
"20.0.1",
"20.0.0",
@@ -404,38 +438,12 @@
{
"name": "lavalink",
"image": "fredboat/lavalink",
"tags": [
"v3.7",
"v3.6",
"v3-vda0b3a4b3916a7b1a2b79702de1143c3a6939810-SNAPSHOT",
"v3-vc92690c425390bd20f6c51643c67ba79ab85b7e0-SNAPSHOT",
"v3-vab81dcd46adf3e8a961dd57eacd2a1bde1233e6c-SNAPSHOT",
"v3-v9c9432704d6a4badfcbd06a57597c54bed8f4326-SNAPSHOT",
"v3-v3.0",
"v3-v3",
"v3-v124f8fae7dab299f9cdf1cb4c1715be455497286-SNAPSHOT",
"v3-",
"v3",
"v2.0.1",
"v2.0",
"v2",
"update-udpqueue-vb4a439d6147dbd8641ea4f265e8efc9f1e16e2d3-SNAPSHOT",
"update-udpqueue-",
"update-udpqueue",
"revert-713-fix-error-for-loading-jda-nas",
"refactor-github-actions",
"patch-update-lp",
"patch-update-github-actions",
"patch-more-configurable-github-actions",
"patch-lavaplayer-update",
"patch-lavaplayer-bump",
"patch-build-number",
"next-api-vd4db194cac7a839a3899857f1f6d7b910369309d-SNAPSHOT",
"next-api-vc2e018d5ffef54b2d17244b3d213e31723a084d6-SNAPSHOT",
"next-api-v42cb5f7c58e98d1911e87bffb35aee0a235b85f8-SNAPSHOT",
"next-api-v31a243bda80badbd7d643f68fc1f87e99639060f-SNAPSHOT",
"next-api-v17f6884434c2d70d1704b2322a951d9f07af8865-SNAPSHOT"
]
"tags": ["3.7.0", "3.6.1", "3.5.1", "v2.0.1"]
},
{
"name": "libretranslate",
"image": "libretranslate/libretranslate",
"tags": ["v1.3.8", "v1.3.6", "v1.3.4", "v1.3.2", "v1.3.0", "v1.2.8"]
},
{
"name": "meilisearch",
@@ -477,6 +485,7 @@
"name": "minio",
"image": "minio/minio",
"tags": [
"RELEASE.2023-01-12T02-06-16Z",
"RELEASE.2023-01-06T18-11-18Z",
"RELEASE.2023-01-02T09-40-09Z",
"RELEASE.2022-12-12T19-27-27Z",
@@ -505,8 +514,7 @@
"RELEASE.2022-09-01T23-53-36Z.fips",
"RELEASE.2022-08-26T19-53-15Z.fips",
"RELEASE.2022-08-25T07-17-05Z.fips",
"RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb",
"RELEASE.2022-08-22T23-53-06Z"
"RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb"
]
},
{
@@ -549,77 +557,56 @@
"name": "nocodb",
"image": "nocodb/nocodb",
"tags": [
"0.101.2",
"0.101.0",
"0.100.1",
"0.99.1",
"0.98.4",
"0.98.2",
"0.98.0",
"0.96.4",
"0.96.2",
"0.96.0",
"0.92.3",
"0.91.10",
"0.91.9",
"0.91.7",
"0.91.0",
"0.90.10",
"0.90.7",
"0.90.4",
"0.90.2",
"0.90.0",
"0.84.15",
"0.84.12",
"0.84.8",
"0.84.6",
"0.84.2",
"0.84.1",
"0.83.6",
"0.83.3",
"0.83.1",
"0.82.0",
"0.81.0",
"0.11.46"
"0.99.2",
"0.99.0",
"0.98.3",
"0.98.1",
"0.97.0",
"0.96.3",
"0.96.1",
"0.92.4",
"0.92.0",
"0.91.8",
"0.91.6",
"0.91.1",
"0.90.11",
"0.90.8",
"0.90.5",
"0.90.3",
"0.90.1",
"0.84.16",
"0.84.14",
"0.84.10",
"0.84.9",
"0.84.7",
"0.84.3",
"0.83.8",
"0.83.5",
"0.83.2",
"0.83.0"
]
},
{
"name": "openblocks",
"image": "openblocksdev/openblocks-ce",
"tags": ["latest", "heroku", "beta", "1.1.3", "1.1.2", "1.1.1", "1.1.0", "1.0.21"]
"tags": ["1.1.3", "1.1.1", "1.0.21"]
},
{
"name": "plausibleanalytics-arm",
"image": "plausible/analytics",
"tags": [
"v1.5.1",
"v1.5.0-rc.2",
"v1.5.0-rc.1",
"v1.5.0",
"v1.5",
"v1.4.4",
"v1.4.3",
"v1.4.2",
"v1.4.1",
"v1.4.0.rc.0",
"v1.4.0-rc.0",
"v1.4.0",
"v1.4",
"v1.3.0-rc.1",
"v1.3.0-rc.0",
"v1.3.0",
"v1.3",
"v1.2.1",
"v1.2.0",
"v1.2-rc.1",
"v1.2-rc.0",
"v1.2",
"v1.1.1",
"v1.1.0",
"v1.1",
"v1.0.0",
"v1.0",
"v1",
"stable",
"master"
"v1.0.0"
]
},
{
@@ -627,55 +614,26 @@
"image": "plausible/analytics",
"tags": [
"v1.5.1",
"v1.5.0-rc.2",
"v1.5.0-rc.1",
"v1.5.0",
"v1.5",
"v1.4.4",
"v1.4.3",
"v1.4.2",
"v1.4.1",
"v1.4.0.rc.0",
"v1.4.0-rc.0",
"v1.4.0",
"v1.4",
"v1.3.0-rc.1",
"v1.3.0-rc.0",
"v1.3.0",
"v1.3",
"v1.2.1",
"v1.2.0",
"v1.2-rc.1",
"v1.2-rc.0",
"v1.2",
"v1.1.1",
"v1.1.0",
"v1.1",
"v1.0.0",
"v1.0",
"v1",
"stable",
"master"
]
},
{
"name": "pocketbase",
"image": "coollabsio/pocketbase",
"tags": [
"0.8.0-arm64",
"0.8.0-amd64",
"0.8.0-aarch64",
"0.8.0",
"0.10.2-arm64",
"0.10.2-amd64",
"0.10.2-aarch64",
"0.10.2"
"v1.0.0"
]
},
{ "name": "pocketbase", "image": "coollabsio/pocketbase", "tags": ["0.11.0", "0.10.2", "0.8.0"] },
{
"name": "searxng",
"image": "searxng/searxng",
"tags": [
"2023.01.15-52d41559",
"2023.01.15-13b0c251",
"2023.01.14-b720a495",
"2023.01.14-449aebae",
"2023.01.14-18d895ff",
"2023.01.09-afd71a6c",
"2023.01.09-a90ed481",
"2023.01.08-54e63839",
@@ -700,18 +658,14 @@
"2022.12.26-0d489617",
"2022.12.23-e8f72d70",
"2022.12.23-a2d506d4",
"2022.12.22-d75ae7c8",
"2022.12.16-f5bd73d9",
"2022.12.16-b9274821",
"2022.12.16-42ca37a6",
"2022.12.16-2a51c856",
"2022.12.16-0dac581c"
"2022.12.22-d75ae7c8"
]
},
{
"name": "trilium",
"image": "zadam/trilium",
"tags": [
"0.58.4",
"0.57.4",
"0.57.2",
"0.56.1",
@@ -740,8 +694,7 @@
"0.45.7",
"0.45.5",
"0.45.3",
"0.44.8",
"0.44.6"
"0.44.8"
]
},
{
@@ -907,6 +860,15 @@
"image": "weblate/weblate",
"tags": [
"latest",
"edge-2023-01-13-e824b551f23c3679467e38b06366744a06aa3b0c",
"edge-2023-01-13-468b996565e6b62edb78d40b515c476e0d860273",
"edge-2023-01-12-fe3d58b14f119eb5501220e9f096949c2e1ec2d3",
"edge-2023-01-12-112f75f9ee9e118ad493215f89742e6e091be8d0",
"edge-2023-01-11-f7bb190993e329d1529694e8cc7f5e0a80ccd615",
"edge-2023-01-11-e8ef3183aa7723f32c2b60c7c3b89910f2c7c593",
"edge-2023-01-11-155231f6cde18a65e3f35093d66dd0ce93aa7154",
"edge-2023-01-10-e47516e4022f87c019e61998b556b69111187aa9",
"edge-2023-01-10-98c6b38c746165adb27b2a8e93a74fa9ab64f17c",
"edge-2023-01-10-1df5c9dd96a6d8650f6881942fecbe33e1884295",
"edge-2023-01-09-7029b7b6c630be7cdac07d1629573dd2b81bc05f",
"edge-2023-01-09-4b05a878aa25b2c544a4e77027769b5934ec561f",
@@ -926,16 +888,24 @@
"edge-2022-12-24-3e1503494ce06ad6ff32f02db1a7d59224e5c860",
"edge-2022-12-21-cac4b09f943fe97700e3a33b7caf23277d2fcc11",
"edge-2022-12-21-3a8dd1bf66a7295f3512346bc1c97d55c5649dcf",
"edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099",
"edge-2022-12-16-318a467d2e529a081e9ea9dbad993c1736ff1a00",
"edge-2022-12-16-1af41ec4bd3838f967d88b68dec8195419e01e6f",
"edge-2022-12-16-02e9d020b01d004655c3af20c68a30f6c4645c1a",
"edge-2022-12-15-a6af1384a0831b17c43da7262f80d0cfbc766835",
"edge-2022-12-15-a1c9f77b301a9e23fc05ef2adc4694cceb632c25",
"edge-2022-12-15-1305f7115ef79b75e638b097772680d9cadbd4d0",
"edge-2022-12-14-b400145f05687e647bd4c8192be99f7f04373fb5",
"edge-2022-12-12-c0db193a3baacd107c5f2c28c6e0af89c3d5afa3",
"edge-2022-12-09-647d40c67cf405870ba71a01584a42cfaec5915f"
"edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099"
]
},
{
"name": "whoogle",
"image": "benbusby/whoogle-search",
"tags": [
"0.8.0",
"0.7.3",
"0.7.1",
"0.6.0",
"0.5.3",
"0.5.1",
"0.4.1",
"0.3.2",
"v0.3.0",
"0.1.2",
"0.1.0"
]
},
{

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +0,0 @@
import { parentPort } from 'node:worker_threads';
import process from 'node:process';
console.log('Hello TypeScript!');
// signal to parent that the job is done
if (parentPort) parentPort.postMessage('done');
// eslint-disable-next-line unicorn/no-process-exit
else process.exit(0);

View File

@@ -1,25 +0,0 @@
import Bree from 'bree';
import path from 'path';
// import Cabin from 'cabin';
import TSBree from '@breejs/ts-worker';
export const isDev = process.env['NODE_ENV'] === 'development';
Bree.extend(TSBree);
const options: any = {
defaultExtension: 'js',
logger: false,
// logger: false,
// workerMessageHandler: async ({ name, message }) => {
// if (name === 'deployApplication' && message?.deploying) {
// if (scheduler.workers.has('autoUpdater') || scheduler.workers.has('cleanupStorage')) {
// scheduler.workers.get('deployApplication').postMessage('cancel')
// }
// }
// },
jobs: [{ name: 'deployApplication' }, { name: 'worker' }]
};
if (isDev) options.root = path.join(__dirname, './jobs');
export const scheduler = new Bree(options);

View File

@@ -1,84 +0,0 @@
import { z } from 'zod';
import { privateProcedure, router } from '../trpc';
import { decrypt } from '../../lib/common';
import { prisma } from '../../prisma';
import { executeCommand } from '../../lib/executeCommand';
import { stopDatabaseContainer, stopTcpHttpProxy } from '../../lib/docker';
export const databasesRouter = router({
status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
const id = input.id;
const teamId = ctx.user?.teamId;
let isRunning = false;
const database = await prisma.database.findFirst({
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
if (database) {
const { destinationDockerId, destinationDocker } = database;
if (destinationDockerId) {
try {
const { stdout } = await executeCommand({
dockerId: destinationDocker.id,
command: `docker inspect --format '{{json .State}}' ${id}`
});
if (JSON.parse(stdout).Running) {
isRunning = true;
}
} catch (error) {
//
}
}
}
return {
isRunning
};
}),
cleanup: privateProcedure.query(async ({ ctx }) => {
const teamId = ctx.user?.teamId;
let databases = await prisma.database.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { settings: true, destinationDocker: true, teams: true }
});
for (const database of databases) {
if (!database?.version) {
const { id } = database;
if (database.destinationDockerId) {
const everStarted = await stopDatabaseContainer(database);
if (everStarted)
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
}
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } });
}
}
return {};
}),
delete: privateProcedure
.input(z.object({ id: z.string(), force: z.boolean() }))
.mutation(async ({ ctx, input }) => {
const { id, force } = input;
const teamId = ctx.user?.teamId;
const database = await prisma.database.findFirst({
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true }
});
if (!force) {
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword)
database.rootUserPassword = decrypt(database.rootUserPassword);
if (database.destinationDockerId) {
const everStarted = await stopDatabaseContainer(database);
if (everStarted)
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
}
}
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } });
return {};
})
});

View File

@@ -1,171 +0,0 @@
import { z } from 'zod';
import { privateProcedure, router } from '../trpc';
import { decrypt, getTemplates, removeService } from '../../lib/common';
import { prisma } from '../../prisma';
import { executeCommand } from '../../lib/executeCommand';
export const servicesRouter = router({
status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
const id = input.id;
const teamId = ctx.user?.teamId;
if (!teamId) {
throw { status: 400, message: 'Team not found.' };
}
const service = await getServiceFromDB({ id, teamId });
const { destinationDockerId } = service;
let payload = {};
if (destinationDockerId) {
const { stdout: containers } = await executeCommand({
dockerId: service.destinationDocker.id,
command: `docker ps -a --filter "label=com.docker.compose.project=${id}" --format '{{json .}}'`
});
if (containers) {
const containersArray = containers.trim().split('\n');
if (containersArray.length > 0 && containersArray[0] !== '') {
const templates = await getTemplates();
let template = templates.find((t: { type: string }) => t.type === service.type);
const templateStr = JSON.stringify(template);
if (templateStr) {
template = JSON.parse(templateStr.replaceAll('$$id', service.id));
}
for (const container of containersArray) {
let isRunning = false;
let isExited = false;
let isRestarting = false;
let isExcluded = false;
const containerObj = JSON.parse(container);
const exclude = template?.services[containerObj.Names]?.exclude;
if (exclude) {
payload[containerObj.Names] = {
status: {
isExcluded: true,
isRunning: false,
isExited: false,
isRestarting: false
}
};
continue;
}
const status = containerObj.State;
if (status === 'running') {
isRunning = true;
}
if (status === 'exited') {
isExited = true;
}
if (status === 'restarting') {
isRestarting = true;
}
payload[containerObj.Names] = {
status: {
isExcluded,
isRunning,
isExited,
isRestarting
}
};
}
}
}
}
return payload;
}),
cleanup: privateProcedure.query(async ({ ctx }) => {
const teamId = ctx.user?.teamId;
let services = await prisma.service.findMany({
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, teams: true }
});
for (const service of services) {
if (!service.fqdn) {
if (service.destinationDockerId) {
const { stdout: containers } = await executeCommand({
dockerId: service.destinationDockerId,
command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}`
});
if (containers) {
const containerArray = containers.split('\n');
if (containerArray.length > 0) {
for (const container of containerArray) {
await executeCommand({
dockerId: service.destinationDockerId,
command: `docker stop -t 0 ${container}`
});
await executeCommand({
dockerId: service.destinationDockerId,
command: `docker rm --force ${container}`
});
}
}
}
}
await removeService({ id: service.id });
}
}
}),
delete: privateProcedure
.input(z.object({ force: z.boolean(), id: z.string() }))
.mutation(async ({ input }) => {
// todo: check if user is allowed to delete service
const { id } = input;
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
await prisma.serviceSetting.deleteMany({ where: { serviceId: id } });
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
await prisma.fider.deleteMany({ where: { serviceId: id } });
await prisma.ghost.deleteMany({ where: { serviceId: id } });
await prisma.umami.deleteMany({ where: { serviceId: id } });
await prisma.hasura.deleteMany({ where: { serviceId: id } });
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
await prisma.minio.deleteMany({ where: { serviceId: id } });
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
await prisma.wordpress.deleteMany({ where: { serviceId: id } });
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
await prisma.moodle.deleteMany({ where: { serviceId: id } });
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
await prisma.searxng.deleteMany({ where: { serviceId: id } });
await prisma.weblate.deleteMany({ where: { serviceId: id } });
await prisma.taiga.deleteMany({ where: { serviceId: id } });
await prisma.service.delete({ where: { id } });
return {};
})
});
export async function getServiceFromDB({
id,
teamId
}: {
id: string;
teamId: string;
}): Promise<any> {
const settings = await prisma.setting.findFirst();
const body = await prisma.service.findFirst({
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: {
destinationDocker: true,
persistentStorage: true,
serviceSecret: true,
serviceSetting: true,
wordpress: true,
plausibleAnalytics: true
}
});
if (!body) {
return null;
}
// body.type = fixType(body.type);
if (body?.serviceSecret.length > 0) {
body.serviceSecret = body.serviceSecret.map((s) => {
s.value = decrypt(s.value);
return s;
});
}
if (body.wordpress) {
body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword);
}
return { ...body, settings };
}

View File

@@ -3,6 +3,15 @@ import { addToast } from './store';
import Cookies from 'js-cookie';
export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay));
export function dashify(str: string, options?: any): string {
if (typeof str !== 'string') return str;
return str
.trim()
.replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-'))
.replace(/^-+|-+$/g, '')
.replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m))
.toLowerCase();
}
export function errorNotification(error: any | { message: string }): void {
if (error instanceof Error) {
console.error(error.message)

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import ExternalLink from './ExternalLink.svelte';
import Tooltip from './Tooltip.svelte';
export let url = 'https://docs.coollabs.io';
export let text: any = '';
export let isExternal = false;
let id =
'cool-' +
url
.split('')
.map((c) => c.charCodeAt(0).toString(16).padStart(2, '0'))
.join('')
.slice(-16);
</script>
<a
{id}
href={url}
target="_blank noreferrer"
class="flex no-underline inline-block cursor-pointer"
class:icons={!text}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"
/>
</svg>
{text}
{#if isExternal}
<ExternalLink />
{/if}
</a>
{#if !text}
<Tooltip triggeredBy={`#${id}`}>See details in the documentation</Tooltip>
{/if}

View File

@@ -0,0 +1,10 @@
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
stroke-width="3"
stroke="currentColor"
class="w-3 h-3 text-white"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25" />
</svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@@ -0,0 +1,6 @@
<script lang="ts">
export let text: string;
export let customClass = 'max-w-[24rem]';
</script>
<div class="p-2 text-xs text-stone-400 {customClass}">{@html text}</div>

View File

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 486 B

View File

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 262 B

Some files were not shown because too many files have changed in this diff Show More