mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-27 12:33:54 +00:00
Compare commits
223 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba9fa442d1 | ||
|
|
87da27f9bf | ||
|
|
b5bc5fe2c6 | ||
|
|
d2329360d0 | ||
|
|
7ece0ae10a | ||
|
|
f931b47eb8 | ||
|
|
7f7eb12ded | ||
|
|
c0940f7a19 | ||
|
|
9dfde11e35 | ||
|
|
6f15cc2dbc | ||
|
|
120308638f | ||
|
|
1d04ef99bb | ||
|
|
9b00d177ef | ||
|
|
884524c448 | ||
|
|
3ae1e7e87d | ||
|
|
81f885311d | ||
|
|
d9362f09d8 | ||
|
|
906d181d1b | ||
|
|
44b8812a7b | ||
|
|
3308c45e88 | ||
|
|
e530ecf9f9 | ||
|
|
51b5edb04f | ||
|
|
f0d89f850e | ||
|
|
b777e08542 | ||
|
|
2e485df530 | ||
|
|
3c37d22a6e | ||
|
|
08ab7a504a | ||
|
|
06563ef921 | ||
|
|
34f6210bc0 | ||
|
|
0bbde0c605 | ||
|
|
a8f24fd1b7 | ||
|
|
c3e0237696 | ||
|
|
bb6925920f | ||
|
|
63ec2a33ae | ||
|
|
c89a959fe8 | ||
|
|
150b50e0ba | ||
|
|
4ef824f665 | ||
|
|
5a56cca0aa | ||
|
|
b9189d7647 | ||
|
|
20226c914b | ||
|
|
434e7f8a09 | ||
|
|
a29d733a02 | ||
|
|
9abe4b967b | ||
|
|
3b6a4ece0f | ||
|
|
28d2471b4d | ||
|
|
d122af9fed | ||
|
|
77271f3856 | ||
|
|
ededfb68a6 | ||
|
|
4a3affdd24 | ||
|
|
8f8ea120d3 | ||
|
|
0fa88009f8 | ||
|
|
4375a807df | ||
|
|
b2d97c5908 | ||
|
|
ec89dd606d | ||
|
|
198508a7c3 | ||
|
|
4845e986bb | ||
|
|
1da8a307fc | ||
|
|
b4886e604e | ||
|
|
e84544136e | ||
|
|
ce70252a69 | ||
|
|
5c56962ea1 | ||
|
|
d2ed53b946 | ||
|
|
a4da80b498 | ||
|
|
9bd01492b1 | ||
|
|
da032941b4 | ||
|
|
c138fcc2e2 | ||
|
|
cbb69b0350 | ||
|
|
a8aed3354d | ||
|
|
e8790a4d4c | ||
|
|
df6ef3aaa0 | ||
|
|
2820d99f7b | ||
|
|
077aa4445a | ||
|
|
23bfc119d9 | ||
|
|
ab712ac637 | ||
|
|
b056826e94 | ||
|
|
6311627899 | ||
|
|
37cea5fb61 | ||
|
|
655a8cd60d | ||
|
|
4c8babc96a | ||
|
|
612bacebed | ||
|
|
ade7c8566d | ||
|
|
19553ce5c8 | ||
|
|
18ed2527e8 | ||
|
|
b0652bc884 | ||
|
|
15c9ad23fe | ||
|
|
578bb12562 | ||
|
|
f82cfda07f | ||
|
|
9e52b2788d | ||
|
|
2e56a113d9 | ||
|
|
4722d777e6 | ||
|
|
2141d54ae0 | ||
|
|
e346225136 | ||
|
|
012d4dae56 | ||
|
|
b4d9fe70af | ||
|
|
85e83b5441 | ||
|
|
6b2a453b8f | ||
|
|
27021538d8 | ||
|
|
8b57a2b055 | ||
|
|
75dd894685 | ||
|
|
9101ef8774 | ||
|
|
5932540630 | ||
|
|
ec376b2e47 | ||
|
|
a176562ad0 | ||
|
|
becf37b676 | ||
|
|
9b5efab8f8 | ||
|
|
e98a8ba599 | ||
|
|
7ddac50008 | ||
|
|
9837ae359f | ||
|
|
710a829dcb | ||
|
|
ccd84fa454 | ||
|
|
335b36d3a9 | ||
|
|
2be30fae00 | ||
|
|
db5cd21884 | ||
|
|
bfd3020031 | ||
|
|
344c36997a | ||
|
|
dfd9272b70 | ||
|
|
359f4520f5 | ||
|
|
aecf014f4e | ||
|
|
d2a89ddf84 | ||
|
|
c01fe153ae | ||
|
|
4f4a838799 | ||
|
|
ac6f2567eb | ||
|
|
05a5816ac6 | ||
|
|
9c8f6e9195 | ||
|
|
2fd001f6d2 | ||
|
|
d641d32413 | ||
|
|
18064ef6a2 | ||
|
|
5cb9216add | ||
|
|
91c36dc810 | ||
|
|
6efb02fa32 | ||
|
|
97313e4180 | ||
|
|
568ab24fd9 | ||
|
|
5a745efcd3 | ||
|
|
c651570e62 | ||
|
|
8980598085 | ||
|
|
c07c742feb | ||
|
|
1053abb9a9 | ||
|
|
2c9e57cbb1 | ||
|
|
c6eaa2c8a6 | ||
|
|
5ab5e913ee | ||
|
|
cea53ca476 | ||
|
|
58af09114b | ||
|
|
c4c0417e2d | ||
|
|
74f90e6947 | ||
|
|
ad5c339780 | ||
|
|
305823db00 | ||
|
|
baf58b298f | ||
|
|
c37367d018 | ||
|
|
1c98796e64 | ||
|
|
e686d9a6ea | ||
|
|
a1936b9d59 | ||
|
|
834f9c9337 | ||
|
|
615f8cfd3b | ||
|
|
8ed134105f | ||
|
|
5d6169b270 | ||
|
|
e83de8b938 | ||
|
|
ee55e039b2 | ||
|
|
086dd89144 | ||
|
|
68e5d4dd2c | ||
|
|
55a35c6bec | ||
|
|
d09b4885fe | ||
|
|
a46773e6d8 | ||
|
|
a422d0220c | ||
|
|
e5eba8430a | ||
|
|
3d235dc316 | ||
|
|
80d3b4be8c | ||
|
|
fe8b7480df | ||
|
|
cebfc3aaa0 | ||
|
|
f778b5a12d | ||
|
|
2244050160 | ||
|
|
9284e42b62 | ||
|
|
ee40120496 | ||
|
|
30cd2149ea | ||
|
|
395df36d57 | ||
|
|
79597ea0e5 | ||
|
|
283f39270a | ||
|
|
7d892bb19d | ||
|
|
a025f124f3 | ||
|
|
84f7287bf8 | ||
|
|
a58544b502 | ||
|
|
4d26175ebe | ||
|
|
78f0e6ff6b | ||
|
|
3af97af634 | ||
|
|
2c2663c8a4 | ||
|
|
1122b8a2f7 | ||
|
|
5b9f38948b | ||
|
|
507eb3b424 | ||
|
|
56fbc0ed6c | ||
|
|
7aaad314e3 | ||
|
|
356949dd54 | ||
|
|
9878baca53 | ||
|
|
9cbc7c2939 | ||
|
|
4680b63911 | ||
|
|
ce4a2d95f2 | ||
|
|
b2e048de8d | ||
|
|
d25a9d7515 | ||
|
|
dc130d3705 | ||
|
|
2391850218 | ||
|
|
c8f7ca920e | ||
|
|
e3e39af6fb | ||
|
|
f38114f5a5 | ||
|
|
1ee9d041df | ||
|
|
9c6f412f04 | ||
|
|
4fa0f2d04a | ||
|
|
e566a66ea4 | ||
|
|
58a42abc67 | ||
|
|
5676bd9d0d | ||
|
|
9691010e7b | ||
|
|
d19be3ad52 | ||
|
|
ec3cbf788b | ||
|
|
1282fd0b76 | ||
|
|
93430e5607 | ||
|
|
14201f4052 | ||
|
|
47979bf16d | ||
|
|
29530f3b17 | ||
|
|
af548e6ef8 | ||
|
|
ed24a9c990 | ||
|
|
0d51b04d79 | ||
|
|
379b1de64f | ||
|
|
f3ff324925 | ||
|
|
0f2160222f | ||
|
|
ce3750c51c | ||
|
|
72a7ea6e91 |
14
.github/workflows/pocketbase-release.yml
vendored
14
.github/workflows/pocketbase-release.yml
vendored
@@ -3,11 +3,11 @@ name: pocketbase-release
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "others/pocketbase"
|
||||
- "others/pocketbase/*"
|
||||
- ".github/workflows/pocketbase-release.yml"
|
||||
branches:
|
||||
- next
|
||||
|
||||
- main
|
||||
jobs:
|
||||
arm64:
|
||||
runs-on: [self-hosted, arm64]
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-arm64
|
||||
tags: coollabsio/pocketbase:0.12.3-arm64
|
||||
amd64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-amd64
|
||||
tags: coollabsio/pocketbase:0.12.3-amd64
|
||||
aarch64:
|
||||
runs-on: [self-hosted, arm64]
|
||||
steps:
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
context: others/pocketbase/
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
tags: coollabsio/pocketbase:0.8.0-aarch64
|
||||
tags: coollabsio/pocketbase:0.12.3-aarch64
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [amd64, arm64, aarch64]
|
||||
@@ -89,5 +89,5 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Create & publish manifest
|
||||
run: |
|
||||
docker manifest create coollabsio/pocketbase:0.8.0 --amend coollabsio/pocketbase:0.8.0-amd64 --amend coollabsio/pocketbase:0.8.0-arm64 --amend coollabsio/pocketbase:0.8.0-aarch64
|
||||
docker manifest push coollabsio/pocketbase:0.8.0
|
||||
docker manifest create coollabsio/pocketbase:0.12.3 --amend coollabsio/pocketbase:0.12.3-amd64 --amend coollabsio/pocketbase:0.12.3-arm64 --amend coollabsio/pocketbase:0.12.3-aarch64
|
||||
docker manifest push coollabsio/pocketbase:0.12.3
|
||||
|
||||
6
.github/workflows/production-release.yml
vendored
6
.github/workflows/production-release.yml
vendored
@@ -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,8 +103,8 @@ 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 push coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||
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 buildx imagetools create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --tag coollabsio/coolify:latest
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
|
||||
90
.github/workflows/release-candidate.yml
vendored
90
.github/workflows/release-candidate.yml
vendored
@@ -1,90 +0,0 @@
|
||||
name: release-candidate
|
||||
on:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
arm64-making-something-cool:
|
||||
runs-on: [self-hosted, arm64]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "next"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Get current package version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
id: package-version
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: coollabsio/coolify:${{github.event.release.name}}-arm64
|
||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64
|
||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64,mode=max
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||
amd64-making-something-cool:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: "next"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Get current package version
|
||||
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||
id: package-version
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: coollabsio/coolify:${{github.event.release.name}}-amd64
|
||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc-amd64
|
||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc-amd64,mode=max
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||
merge-manifest-to-be-cool:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [arm64-making-something-cool, amd64-making-something-cool]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Create & publish manifest
|
||||
run: |
|
||||
docker manifest create coollabsio/coolify:${{github.event.release.name}} --amend coollabsio/coolify:${{github.event.release.name}}-amd64 --amend coollabsio/coolify:${{github.event.release.name}}-arm64
|
||||
docker manifest push coollabsio/coolify:${{github.event.release.name}}
|
||||
|
||||
5
.github/workflows/staging-release.yml
vendored
5
.github/workflows/staging-release.yml
vendored
@@ -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:
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,7 +1,8 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
.pnpm-store
|
||||
build
|
||||
/apps/ui/build
|
||||
/build
|
||||
.svelte-kit
|
||||
package
|
||||
.env
|
||||
@@ -11,9 +12,10 @@ dist
|
||||
apps/api/db/*.db
|
||||
apps/api/db/migration.db-journal
|
||||
apps/api/core*
|
||||
apps/server/build
|
||||
apps/backup/backups/*
|
||||
!apps/backup/backups/.gitkeep
|
||||
logs
|
||||
/logs
|
||||
others/certificates
|
||||
backups/*
|
||||
!backups/.gitkeep
|
||||
|
||||
@@ -10,7 +10,7 @@ You'll need a set of skills to [get started](docs/contribution/GettingStarted.md
|
||||
|
||||
## 1) Setup your development environment
|
||||
|
||||
- 🌟 [Container based](docs/dev_setup/Container.md) ← *Recomended*
|
||||
- 🌟 [Container based](docs/dev_setup/Container.md) ← *Recommended*
|
||||
- 📦 [DockerContainer](docs/dev_setup/DockerContiner.md) *WIP
|
||||
- 🐙 [Github Codespaces](docs/dev_setup/GithubCodespaces.md)
|
||||
- ☁️ [GitPod](docs/dev_setup/GitPod.md)
|
||||
@@ -20,7 +20,7 @@ You'll need a set of skills to [get started](docs/contribution/GettingStarted.md
|
||||
|
||||
- [Install Pnpm](https://pnpm.io/installation)
|
||||
- [Install Docker Engine](https://docs.docker.com/engine/install/)
|
||||
- [Setup Docker Compose Plugin](https://docs.docker.com/compose/install/compose-plugin/)
|
||||
- [Setup Docker Compose Plugin](https://docs.docker.com/compose/install/)
|
||||
- [Setup GIT LFS Support](https://git-lfs.github.com/)
|
||||
|
||||
## 3) Setup Coolify
|
||||
@@ -28,12 +28,12 @@ You'll need a set of skills to [get started](docs/contribution/GettingStarted.md
|
||||
- Copy `apps/api/.env.example` to `apps/api/.env`
|
||||
- Edit `apps/api/.env`, set the `COOLIFY_APP_ID` environment variable to something cool.
|
||||
- Run `pnpm install` to install dependencies.
|
||||
- Run `pnpm db:push` to o create a local SQlite database. This will apply all migrations at `db/dev.db`.
|
||||
- Run `pnpm db:push` to create a local SQlite database. This will apply all migrations at `db/dev.db`.
|
||||
- Run `pnpm db:seed` seed the database.
|
||||
- Run `pnpm dev` start coding.
|
||||
|
||||
```sh
|
||||
# Or... Copy and paste commands bellow:
|
||||
# Or... Copy and paste commands below:
|
||||
cp apps/api/.env.example apps/api/.env
|
||||
pnpm install
|
||||
pnpm db:push
|
||||
@@ -45,4 +45,4 @@ pnpm dev
|
||||
|
||||
You should be able to access `http://localhost:3000`.
|
||||
|
||||
1. Click `Register` and setup your first user.
|
||||
1. Click `Register` and setup your first user.
|
||||
|
||||
@@ -22,7 +22,7 @@ ARG DOCKER_VERSION=20.10.18
|
||||
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
|
||||
ARG DOCKER_COMPOSE_VERSION=2.6.1
|
||||
# https://github.com/buildpacks/pack/releases
|
||||
ARG PACK_VERSION=v0.27.0
|
||||
ARG PACK_VERSION=0.27.0
|
||||
|
||||
RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3
|
||||
RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
|
||||
@@ -50,4 +50,4 @@ RUN pnpm install -p
|
||||
|
||||
EXPOSE 3000
|
||||
ENV CHECKPOINT_DISABLE=1
|
||||
CMD pnpm start
|
||||
CMD pnpm start
|
||||
|
||||
@@ -9,7 +9,7 @@ ARG DOCKER_VERSION=20.10.18
|
||||
# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug.
|
||||
ARG DOCKER_COMPOSE_VERSION=2.6.1
|
||||
# https://github.com/buildpacks/pack/releases
|
||||
ARG PACK_VERSION=v0.27.0
|
||||
ARG PACK_VERSION=0.27.0
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||
@@ -28,4 +28,4 @@ RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /
|
||||
RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack
|
||||
|
||||
EXPOSE 3000
|
||||
ENV CHECKPOINT_DISABLE=1
|
||||
ENV CHECKPOINT_DISABLE=1
|
||||
|
||||
18
README.md
18
README.md
@@ -119,7 +119,7 @@ Learn how to contribute to Coolify as as ...
|
||||
|
||||
<!--
|
||||
→ 🧑🏽🎨 Designer
|
||||
→ 🙋♀️ Community Managemer
|
||||
→ 🙋♀️ Community Manager
|
||||
→ 🧙🏻♂️ Text Content Creator
|
||||
→ 👨🏼🎤 Video Content Creator
|
||||
-->
|
||||
@@ -130,12 +130,12 @@ Learn how to contribute to Coolify as as ...
|
||||
|
||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
||||
|
||||
### Individuals
|
||||
|
||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||
|
||||
### Organizations
|
||||
|
||||
Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
|
||||

|
||||
|
||||
Support this project with your organization. Your logo will show up here with a link to your website.
|
||||
|
||||
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
||||
@@ -148,3 +148,11 @@ Support this project with your organization. Your logo will show up here with a
|
||||
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||
|
||||
### Individuals
|
||||
|
||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#coollabsio/coolify&Date)
|
||||
|
||||
4
apps/api/.gitignore
vendored
4
apps/api/.gitignore
vendored
@@ -8,4 +8,6 @@ package
|
||||
!.env.example
|
||||
dist
|
||||
dev.db
|
||||
client
|
||||
client
|
||||
testTemplate.yaml
|
||||
testTags.json
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,5 +1,229 @@
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: "0.8.0"
|
||||
defaultVersion: "9.22"
|
||||
documentation: https://docs.directus.io/getting-started/introduction.html
|
||||
type: directus-postgresql
|
||||
name: Directus
|
||||
subname: (PostgreSQL)
|
||||
description: >-
|
||||
Directus is a free and open-source headless CMS framework for managing custom SQL-based databases.
|
||||
labels:
|
||||
- CMS
|
||||
- headless
|
||||
services:
|
||||
$$id:
|
||||
name: Directus
|
||||
depends_on:
|
||||
- $$id-postgresql
|
||||
- $$id-redis
|
||||
image: directus/directus:$$core_version
|
||||
volumes:
|
||||
- $$id-uploads:/directus/uploads
|
||||
- $$id-database:/directus/database
|
||||
- $$id-extensions:/directus/extensions
|
||||
environment:
|
||||
- KEY=$$secret_key
|
||||
- SECRET=$$secret_secret
|
||||
- DB_CLIENT=pg
|
||||
- DB_CONNECTION_STRING=$$secret_db_connection_string
|
||||
- CACHE_ENABLED=true
|
||||
- CACHE_STORE=redis
|
||||
- CACHE_REDIS=$$secret_cache_redis
|
||||
- ADMIN_EMAIL=$$config_admin_email
|
||||
- ADMIN_PASSWORD=$$secret_admin_password
|
||||
- CACHE_AUTO_PURGE=true
|
||||
- PUBLIC_URL=$$config_public_url
|
||||
ports:
|
||||
- "8055"
|
||||
$$id-postgresql:
|
||||
name: Directus PostgreSQL
|
||||
depends_on: []
|
||||
image: postgres:14-alpine
|
||||
volumes:
|
||||
- $$id-postgresql-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=$$config_postgres_user
|
||||
- POSTGRES_PASSWORD=$$secret_postgres_password
|
||||
- POSTGRES_DB=$$config_postgres_db
|
||||
ports: []
|
||||
$$id-redis:
|
||||
name: Directus Redis
|
||||
depends_on: []
|
||||
image: redis:7.0.4-alpine
|
||||
command: "--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5"
|
||||
volumes:
|
||||
- "$$id-redis:/data"
|
||||
environment: []
|
||||
variables:
|
||||
- id: $$config_public_url
|
||||
name: PUBLIC_URL
|
||||
label: Public URL
|
||||
defaultValue: $$generate_fqdn
|
||||
description: ""
|
||||
- id: $$secret_db_connection_string
|
||||
name: DB_CONNECTION_STRING
|
||||
label: Directus Database Url
|
||||
defaultValue: postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db
|
||||
description: ""
|
||||
- id: $$config_postgres_db
|
||||
main: $$id-postgresql
|
||||
name: POSTGRES_DB
|
||||
label: Database
|
||||
defaultValue: directus
|
||||
description: ""
|
||||
- id: $$config_postgres_user
|
||||
main: $$id-postgresql
|
||||
name: POSTGRES_USER
|
||||
label: User
|
||||
defaultValue: $$generate_username
|
||||
description: ""
|
||||
- id: $$secret_postgres_password
|
||||
main: $$id-postgresql
|
||||
name: POSTGRES_PASSWORD
|
||||
label: Password
|
||||
defaultValue: $$generate_password
|
||||
description: ""
|
||||
showOnConfiguration: true
|
||||
- id: $$secret_cache_redis
|
||||
name: CACHE_REDIS
|
||||
label: Redis Url
|
||||
defaultValue: redis://$$id-redis:6379
|
||||
description: ""
|
||||
- id: $$config_admin_email
|
||||
name: ADMIN_EMAIL
|
||||
label: Initial Admin Email
|
||||
defaultValue: "admin@example.com"
|
||||
description: "The email address of the first user that is automatically created. You can change it later in Directus."
|
||||
- id: $$secret_admin_password
|
||||
name: ADMIN_PASSWORD
|
||||
label: Initial Admin Password
|
||||
defaultValue: $$generate_password
|
||||
description: "The password of the first user that is automatically created."
|
||||
showOnConfiguration: true
|
||||
- id: $$secret_key
|
||||
name: KEY
|
||||
label: Key
|
||||
defaultValue: $$generate_password
|
||||
description: "Unique identifier for the project."
|
||||
showOnConfiguration: true
|
||||
- id: $$secret_secret
|
||||
name: SECRET
|
||||
label: Secret
|
||||
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
|
||||
description: Free and Open Source Machine Translation API. 100% self-hosted, offline capable and easy to setup.
|
||||
type: libretranslate
|
||||
name: Libretranslate
|
||||
labels:
|
||||
- translator
|
||||
- argos
|
||||
- python
|
||||
- libretranslate
|
||||
services:
|
||||
$$id:
|
||||
name: Libretranslate
|
||||
image: libretranslate/libretranslate:$$core_version
|
||||
environment:
|
||||
- LT_HOST=0.0.0.0
|
||||
- LT_SUGGESTIONS=true
|
||||
- LT_CHAR_LIMIT=$$config_lt_char_limit
|
||||
- LT_REQ_LIMIT=$$config_lt_req_limit
|
||||
- LT_BATCH_LIMIT=$$config_lt_batch_limit
|
||||
- LT_GA_ID=$$config_lt_ga_id
|
||||
- LT_DISABLE_WEB_UI=$$config_lt_web_ui
|
||||
volumes:
|
||||
- $$id-libretranslate:/libretranslate
|
||||
ports:
|
||||
- "5000"
|
||||
variables:
|
||||
- id: $$config_lt_char_limit
|
||||
name: LT_CHAR_LIMIT
|
||||
label: Char limit
|
||||
defaultValue: "5000"
|
||||
description: "Set character limit."
|
||||
- id: $$config_lt_req_limit
|
||||
name: LT_REQ_LIMIT
|
||||
label: Request limit
|
||||
defaultValue: "5000"
|
||||
description: "Set maximum number of requests per minute per client."
|
||||
- id: $$config_lt_batch_limit
|
||||
name: LT_BATCH_LIMIT
|
||||
label: Batch Limit
|
||||
defaultValue: "5000"
|
||||
description: "Set maximum number of texts to translate in a batch request."
|
||||
- id: $$config_lt_ga_id
|
||||
name: LT_GA_ID
|
||||
label: Google Analytics ID
|
||||
defaultValue: ""
|
||||
description: "Enable Google Analytics on the API client page by providing an ID"
|
||||
- id: $$config_lt_web_ui
|
||||
name: LT_DISABLE_WEB_UI
|
||||
label: Web UI
|
||||
defaultValue: "false"
|
||||
description: "Disable or enable web ui. True or false."
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.8.0
|
||||
documentation: https://github.com/benbusby/whoogle-search
|
||||
type: whoogle
|
||||
name: Whoogle Search
|
||||
description: A self-hosted, ad-free, privacy-respecting metasearch engine
|
||||
labels:
|
||||
- search
|
||||
- google
|
||||
services:
|
||||
$$id:
|
||||
name: Whoogle Search
|
||||
documentation: https://github.com/benbusby/whoogle-search
|
||||
depends_on: []
|
||||
image: benbusby/whoogle-search:$$core_version
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
- WHOOGLE_USER=$$config_whoogle_username
|
||||
- WHOOGLE_PASS=$$secret_whoogle_password
|
||||
- WHOOGLE_CONFIG_PREFERENCES_KEY=$$secret_whoogle_preferences_key
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
ports:
|
||||
- "5000"
|
||||
variables:
|
||||
- id: $$config_whoogle_username
|
||||
name: WHOOGLE_USER
|
||||
label: Whoogle User
|
||||
defaultValue: $$generate_username
|
||||
description: "Username to log into Whoogle"
|
||||
- id: $$secret_whoogle_password
|
||||
name: WHOOGLE_PASSWORD
|
||||
label: Whoogle Password
|
||||
defaultValue: $$generate_password
|
||||
description: "Password to log into Whoogle"
|
||||
showOnConfiguration: true
|
||||
- id: $$secret_whoogle_preferences_key
|
||||
name: WHOOGLE_CONFIG_PREFERENCES_KEY
|
||||
label: Whoogle preferences key
|
||||
defaultValue: $$generate_password
|
||||
description: "password to encrypt preferences"
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 1.1.5
|
||||
documentation: https://docs.openblocks.dev/
|
||||
type: openblocks
|
||||
name: Openblocks
|
||||
description: The Open Source Retool Alternative
|
||||
services:
|
||||
$$id:
|
||||
image: openblocksdev/openblocks-ce:$$core_version
|
||||
volumes:
|
||||
- $$id-stacks-data:/openblocks-stacks
|
||||
ports:
|
||||
- "3000"
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: "0.12.3"
|
||||
documentation: https://pocketbase.io/docs/
|
||||
type: pocketbase
|
||||
name: Pocketbase
|
||||
@@ -124,12 +348,12 @@
|
||||
description: ""
|
||||
- id: $$config_disable_auth
|
||||
name: DISABLE_AUTH
|
||||
label: Disable Authentication
|
||||
label: Authentication
|
||||
defaultValue: "false"
|
||||
description: ""
|
||||
- id: $$config_disable_registration
|
||||
name: DISABLE_REGISTRATION
|
||||
label: Disable Registration
|
||||
label: Registration
|
||||
defaultValue: "true"
|
||||
description: ""
|
||||
- id: $$config_postgres_user
|
||||
@@ -157,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
|
||||
@@ -332,12 +556,12 @@
|
||||
volumes:
|
||||
- $$id-lavalink:/lavalink
|
||||
ports:
|
||||
- "2333"
|
||||
- $$config_port
|
||||
files:
|
||||
- location: /opt/Lavalink/application.yml
|
||||
content: >-
|
||||
server:
|
||||
port: $$config_port
|
||||
port: 2333
|
||||
address: 0.0.0.0
|
||||
lavalink:
|
||||
server:
|
||||
@@ -364,18 +588,13 @@
|
||||
max-file-size: 1GB
|
||||
max-history: 30
|
||||
variables:
|
||||
- id: $$config_port
|
||||
name: PORT
|
||||
label: Port
|
||||
defaultValue: "2333"
|
||||
required: true
|
||||
- id: $$secret_password
|
||||
name: PASSWORD
|
||||
label: Password
|
||||
defaultValue: $$generate_password
|
||||
required: true
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: v1.8.9
|
||||
defaultVersion: v1.9.6
|
||||
documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/
|
||||
type: appsmith
|
||||
name: Appsmith
|
||||
@@ -408,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:
|
||||
@@ -428,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:
|
||||
@@ -445,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
|
||||
@@ -460,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
|
||||
@@ -478,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
|
||||
@@ -499,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
|
||||
@@ -1669,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
|
||||
@@ -1742,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
|
||||
@@ -1964,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
|
||||
@@ -2444,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
|
||||
@@ -2474,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
|
||||
@@ -2592,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
|
||||
@@ -2656,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
|
||||
@@ -2733,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
|
||||
@@ -2823,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
|
||||
@@ -2897,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
|
||||
@@ -2912,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"
|
||||
@@ -2928,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
|
||||
@@ -2987,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
|
||||
@@ -3106,7 +3324,7 @@
|
||||
defaultValue: $$generate_username
|
||||
description: ""
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.207.0
|
||||
defaultVersion: 0.215.1
|
||||
documentation: https://docs.n8n.io
|
||||
type: n8n
|
||||
name: n8n.io
|
||||
@@ -3137,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
|
||||
@@ -3250,12 +3468,12 @@
|
||||
description: ""
|
||||
- id: $$config_disable_auth
|
||||
name: DISABLE_AUTH
|
||||
label: Disable Authentication
|
||||
label: Authentication
|
||||
defaultValue: "false"
|
||||
description: ""
|
||||
- id: $$config_disable_registration
|
||||
name: DISABLE_REGISTRATION
|
||||
label: Disable Registration
|
||||
label: Registration
|
||||
defaultValue: "true"
|
||||
description: ""
|
||||
- id: $$config_postgresql_username
|
||||
@@ -3283,7 +3501,7 @@
|
||||
defaultValue: plausible.js
|
||||
description: This is the default script name.
|
||||
- templateVersion: 1.0.0
|
||||
defaultVersion: 0.99.1
|
||||
defaultVersion: 0.104.2
|
||||
documentation: https://docs.nocodb.com
|
||||
type: nocodb
|
||||
name: NocoDB
|
||||
|
||||
@@ -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/multipart": "7.3.0",
|
||||
"@fastify/static": "6.5.1",
|
||||
"@fastify/env": "4.2.0",
|
||||
"@fastify/jwt": "6.5.0",
|
||||
"@fastify/multipart": "7.4.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",
|
||||
@@ -48,36 +48,36 @@
|
||||
"is-ip": "5.0.0",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsonwebtoken": "9.0.0",
|
||||
"minimist": "^1.2.7",
|
||||
"node-forge": "1.3.1",
|
||||
"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"
|
||||
|
||||
@@ -8,6 +8,7 @@ CREATE TABLE "new_GitSource" (
|
||||
"apiUrl" TEXT,
|
||||
"htmlUrl" TEXT,
|
||||
"customPort" INTEGER NOT NULL DEFAULT 22,
|
||||
"customUser" TEXT NOT NULL DEFAULT 'git',
|
||||
"organization" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
@@ -17,7 +18,7 @@ CREATE TABLE "new_GitSource" (
|
||||
CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||
INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt" FROM "GitSource";
|
||||
DROP TABLE "GitSource";
|
||||
ALTER TABLE "new_GitSource" RENAME TO "GitSource";
|
||||
CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId");
|
||||
@@ -10,11 +10,13 @@ CREATE TABLE "new_ApplicationSettings" (
|
||||
"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", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings";
|
||||
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");
|
||||
@@ -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])
|
||||
@@ -325,6 +326,7 @@ model GitSource {
|
||||
apiUrl String?
|
||||
htmlUrl String?
|
||||
customPort Int @default(22)
|
||||
customUser String @default("git")
|
||||
organization String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -6,14 +6,27 @@ import cookie from '@fastify/cookie';
|
||||
import multipart from '@fastify/multipart';
|
||||
import path, { join } from 'path';
|
||||
import autoLoad from '@fastify/autoload';
|
||||
import socketIO from 'fastify-socket.io'
|
||||
import socketIOServer from './realtime'
|
||||
import socketIO from 'fastify-socket.io';
|
||||
import socketIOServer from './realtime';
|
||||
|
||||
import { cleanupDockerStorage, createRemoteEngineConfiguration, decrypt, executeCommand, generateDatabaseConfiguration, isDev, listSettings, prisma, sentryDSN, startTraefikProxy, startTraefikTCPProxy, version } from './lib/common';
|
||||
import {
|
||||
cleanupDockerStorage,
|
||||
createRemoteEngineConfiguration,
|
||||
decrypt,
|
||||
executeCommand,
|
||||
generateDatabaseConfiguration,
|
||||
isDev,
|
||||
listSettings,
|
||||
prisma,
|
||||
sentryDSN,
|
||||
startTraefikProxy,
|
||||
startTraefikTCPProxy,
|
||||
version
|
||||
} from './lib/common';
|
||||
import { scheduler } from './lib/scheduler';
|
||||
import { compareVersions } from 'compare-versions';
|
||||
import Graceful from '@ladjs/graceful'
|
||||
import yaml from 'js-yaml'
|
||||
import Graceful from '@ladjs/graceful';
|
||||
import yaml from 'js-yaml';
|
||||
import fs from 'fs/promises';
|
||||
import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers';
|
||||
import { checkContainer } from './lib/docker';
|
||||
@@ -23,13 +36,13 @@ import * as Sentry from '@sentry/node';
|
||||
declare module 'fastify' {
|
||||
interface FastifyInstance {
|
||||
config: {
|
||||
COOLIFY_APP_ID: string,
|
||||
COOLIFY_SECRET_KEY: string,
|
||||
COOLIFY_DATABASE_URL: string,
|
||||
COOLIFY_IS_ON: string,
|
||||
COOLIFY_WHITE_LABELED: string,
|
||||
COOLIFY_WHITE_LABELED_ICON: string | null,
|
||||
COOLIFY_AUTO_UPDATE: string,
|
||||
COOLIFY_APP_ID: string;
|
||||
COOLIFY_SECRET_KEY: string;
|
||||
COOLIFY_DATABASE_URL: string;
|
||||
COOLIFY_IS_ON: string;
|
||||
COOLIFY_WHITE_LABELED: string;
|
||||
COOLIFY_WHITE_LABELED_ICON: string | null;
|
||||
COOLIFY_AUTO_UPDATE: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -38,7 +51,7 @@ const port = isDev ? 3001 : 3000;
|
||||
const host = '0.0.0.0';
|
||||
|
||||
(async () => {
|
||||
const settings = await prisma.setting.findFirst()
|
||||
const settings = await prisma.setting.findFirst();
|
||||
const fastify = Fastify({
|
||||
logger: settings?.isAPIDebuggingEnabled || false,
|
||||
trustProxy: true
|
||||
@@ -49,10 +62,10 @@ const host = '0.0.0.0';
|
||||
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
||||
properties: {
|
||||
COOLIFY_APP_ID: {
|
||||
type: 'string',
|
||||
type: 'string'
|
||||
},
|
||||
COOLIFY_SECRET_KEY: {
|
||||
type: 'string',
|
||||
type: 'string'
|
||||
},
|
||||
COOLIFY_DATABASE_URL: {
|
||||
type: 'string',
|
||||
@@ -73,8 +86,7 @@ const host = '0.0.0.0';
|
||||
COOLIFY_AUTO_UPDATE: {
|
||||
type: 'string',
|
||||
default: 'false'
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
const options = {
|
||||
@@ -103,13 +115,13 @@ const host = '0.0.0.0';
|
||||
fastify.register(autoLoad, {
|
||||
dir: join(__dirname, 'routes')
|
||||
});
|
||||
fastify.register(cookie)
|
||||
fastify.register(cookie);
|
||||
fastify.register(cors);
|
||||
fastify.register(socketIO, {
|
||||
cors: {
|
||||
origin: isDev ? "*" : ''
|
||||
origin: isDev ? '*' : ''
|
||||
}
|
||||
})
|
||||
});
|
||||
// To detect allowed origins
|
||||
// fastify.addHook('onRequest', async (request, reply) => {
|
||||
// console.log(request.headers.host)
|
||||
@@ -131,10 +143,9 @@ const host = '0.0.0.0';
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
try {
|
||||
await fastify.listen({ port, host })
|
||||
await socketIOServer(fastify)
|
||||
await fastify.listen({ port, host });
|
||||
await socketIOServer(fastify);
|
||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||
|
||||
migrateServicesToNewTemplate();
|
||||
@@ -148,105 +159,125 @@ const host = '0.0.0.0';
|
||||
if (!scheduler.workers.has('deployApplication')) {
|
||||
scheduler.run('deployApplication');
|
||||
}
|
||||
}, 2000)
|
||||
}, 2000);
|
||||
|
||||
// autoUpdater
|
||||
setInterval(async () => {
|
||||
await autoUpdater()
|
||||
}, 60000 * 15)
|
||||
await autoUpdater();
|
||||
}, 60000 * 15);
|
||||
|
||||
// cleanupStorage
|
||||
setInterval(async () => {
|
||||
await cleanupStorage()
|
||||
}, 60000 * 10)
|
||||
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();
|
||||
await checkFluentBit();
|
||||
}, 60000)
|
||||
}, 60000);
|
||||
|
||||
// Refresh and check templates
|
||||
setInterval(async () => {
|
||||
await refreshTemplates()
|
||||
}, 60000)
|
||||
await refreshTemplates();
|
||||
}, 60000);
|
||||
|
||||
setInterval(async () => {
|
||||
await refreshTags()
|
||||
}, 60000)
|
||||
await refreshTags();
|
||||
}, 60000);
|
||||
|
||||
setInterval(async () => {
|
||||
await migrateServicesToNewTemplate()
|
||||
}, isDev ? 10000 : 60000)
|
||||
setInterval(
|
||||
async () => {
|
||||
await migrateServicesToNewTemplate();
|
||||
},
|
||||
isDev ? 10000 : 60000
|
||||
);
|
||||
|
||||
setInterval(async () => {
|
||||
await copySSLCertificates();
|
||||
}, 10000)
|
||||
}, 10000);
|
||||
|
||||
await Promise.all([
|
||||
getTagsTemplates(),
|
||||
getArch(),
|
||||
getIPAddress(),
|
||||
configureRemoteDockers(),
|
||||
])
|
||||
|
||||
configureRemoteDockers()
|
||||
// cleanupStuckedContainers()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
async function getIPAddress() {
|
||||
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
||||
const { publicIpv4, publicIpv6 } = await import('public-ip');
|
||||
try {
|
||||
const settings = await listSettings();
|
||||
if (!settings.ipv4) {
|
||||
const ipv4 = await publicIpv4({ timeout: 2000 })
|
||||
const ipv4 = await publicIpv4({ timeout: 2000 });
|
||||
console.log(`Getting public IPv4 address...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } });
|
||||
}
|
||||
|
||||
if (!settings.ipv6) {
|
||||
const ipv6 = await publicIpv6({ timeout: 2000 })
|
||||
const ipv6 = await publicIpv6({ timeout: 2000 });
|
||||
console.log(`Getting public IPv6 address...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } });
|
||||
}
|
||||
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
}
|
||||
async function getTagsTemplates() {
|
||||
const { default: got } = await import('got')
|
||||
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')
|
||||
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...')
|
||||
} else {
|
||||
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text()
|
||||
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)))
|
||||
await fs.writeFile('/app/tags.json', tags)
|
||||
console.log('[004] Tags and templates loaded...')
|
||||
}
|
||||
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...');
|
||||
} else {
|
||||
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
|
||||
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)));
|
||||
await fs.writeFile('/app/tags.json', tags);
|
||||
console.log('[004] Tags and templates loaded...');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Couldn't get latest templates.")
|
||||
console.log(error)
|
||||
console.log("Couldn't get latest templates.");
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
async function initServer() {
|
||||
const appId = process.env['COOLIFY_APP_ID'];
|
||||
const settings = await prisma.setting.findUnique({ where: { id: '0' } })
|
||||
const settings = await prisma.setting.findUnique({ where: { id: '0' } });
|
||||
try {
|
||||
if (settings.doNotTrack === true) {
|
||||
console.log('[000] Telemetry disabled...')
|
||||
|
||||
console.log('[000] Telemetry disabled...');
|
||||
} else {
|
||||
if (settings.sentryDSN !== sentryDSN) {
|
||||
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } })
|
||||
await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } });
|
||||
}
|
||||
// Initialize Sentry
|
||||
// Sentry.init({
|
||||
@@ -257,37 +288,83 @@ async function initServer() {
|
||||
// console.log('[000] Sentry initialized...')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
console.log(`[001] Initializing server...`);
|
||||
await executeCommand({ command: `docker network create --attachable coolify` });
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
try {
|
||||
console.log(`[002] Cleanup stucked builds...`);
|
||||
const isOlder = compareVersions('3.8.1', version);
|
||||
if (isOlder === 1) {
|
||||
await prisma.build.updateMany({ where: { status: { in: ['running', 'queued'] } }, data: { status: 'failed' } });
|
||||
await prisma.build.updateMany({
|
||||
where: { status: { in: ['running', 'queued'] } },
|
||||
data: { status: 'failed' }
|
||||
});
|
||||
}
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
try {
|
||||
console.log('[003] Cleaning up old build sources under /tmp/build-sources/...');
|
||||
await fs.rm('/tmp/build-sources', { recursive: true, force: true })
|
||||
await fs.rm('/tmp/build-sources', { recursive: true, force: true });
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function getArch() {
|
||||
try {
|
||||
const settings = await prisma.setting.findFirst({})
|
||||
const settings = await prisma.setting.findFirst({});
|
||||
if (settings && !settings.arch) {
|
||||
console.log(`Getting architecture...`);
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } })
|
||||
await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } });
|
||||
}
|
||||
} catch (error) { }
|
||||
} 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({
|
||||
@@ -296,37 +373,44 @@ async function configureRemoteDockers() {
|
||||
if (remoteDocker.length > 0) {
|
||||
console.log(`Verifying Remote Docker Engines...`);
|
||||
for (const docker of remoteDocker) {
|
||||
console.log('Verifying:', docker.remoteIpAddress)
|
||||
console.log('Verifying:', docker.remoteIpAddress);
|
||||
await verifyRemoteDockerEngineFn(docker.id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function autoUpdater() {
|
||||
try {
|
||||
const { default: got } = await import('got')
|
||||
const { default: got } = await import('got');
|
||||
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 (isUpdateAvailable === 1) {
|
||||
const activeCount = 0
|
||||
const activeCount = 0;
|
||||
if (activeCount === 0) {
|
||||
if (!isDev) {
|
||||
const { isAutoUpdateEnabled } = await prisma.setting.findFirst();
|
||||
if (isAutoUpdateEnabled) {
|
||||
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: `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"`
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('Updating (not really in dev mode).');
|
||||
@@ -334,7 +418,7 @@ async function autoUpdater() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,14 +429,18 @@ async function checkFluentBit() {
|
||||
const { id } = await prisma.destinationDocker.findFirst({
|
||||
where: { engine, network: 'coolify' }
|
||||
});
|
||||
const { found } = await checkContainer({ dockerId: id, container: 'coolify-fluentbit', remove: true });
|
||||
const { found } = await checkContainer({
|
||||
dockerId: id,
|
||||
container: 'coolify-fluentbit',
|
||||
remove: true
|
||||
});
|
||||
if (!found) {
|
||||
await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` });
|
||||
await executeCommand({ command: `docker compose up -d fluent-bit` });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
async function checkProxies() {
|
||||
@@ -368,7 +456,7 @@ async function checkProxies() {
|
||||
where: { engine, network: 'coolify', isCoolifyProxyUsed: true }
|
||||
});
|
||||
if (localDocker) {
|
||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
|
||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 });
|
||||
if (!portReachable) {
|
||||
await startTraefikProxy(localDocker.id);
|
||||
}
|
||||
@@ -380,14 +468,14 @@ async function checkProxies() {
|
||||
if (remoteDocker.length > 0) {
|
||||
for (const docker of remoteDocker) {
|
||||
if (docker.isCoolifyProxyUsed) {
|
||||
portReachable = await isReachable(80, { host: docker.remoteIpAddress })
|
||||
portReachable = await isReachable(80, { host: docker.remoteIpAddress });
|
||||
if (!portReachable) {
|
||||
await startTraefikProxy(docker.id);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await createRemoteEngineConfiguration(docker.id)
|
||||
} catch (error) { }
|
||||
await createRemoteEngineConfiguration(docker.id);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
// TCP Proxies
|
||||
@@ -426,80 +514,109 @@ async function checkProxies() {
|
||||
// await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000);
|
||||
// }
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
async function copySSLCertificates() {
|
||||
try {
|
||||
const pAll = await import('p-all');
|
||||
const actions = []
|
||||
const certificates = await prisma.certificate.findMany({ include: { team: true } })
|
||||
const teamIds = certificates.map(c => c.teamId)
|
||||
const destinations = await prisma.destinationDocker.findMany({ where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } } })
|
||||
const actions = [];
|
||||
const certificates = await prisma.certificate.findMany({ include: { team: true } });
|
||||
const teamIds = certificates.map((c) => c.teamId);
|
||||
const destinations = await prisma.destinationDocker.findMany({
|
||||
where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } }
|
||||
});
|
||||
for (const certificate of certificates) {
|
||||
const { id, key, cert } = certificate
|
||||
const decryptedKey = decrypt(key)
|
||||
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey)
|
||||
await fs.writeFile(`/tmp/${id}-cert.pem`, cert)
|
||||
const { id, key, cert } = certificate;
|
||||
const decryptedKey = decrypt(key);
|
||||
await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey);
|
||||
await fs.writeFile(`/tmp/${id}-cert.pem`, cert);
|
||||
for (const destination of destinations) {
|
||||
if (destination.remoteEngine) {
|
||||
if (destination.remoteVerified) {
|
||||
const { id: dockerId, remoteIpAddress } = destination
|
||||
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress))
|
||||
const { id: dockerId, remoteIpAddress } = destination;
|
||||
actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress));
|
||||
}
|
||||
} else {
|
||||
actions.push(async () => copyLocalCertificates(id))
|
||||
actions.push(async () => copyLocalCertificates(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
await pAll.default(actions, { concurrency: 1 })
|
||||
await pAll.default(actions, { concurrency: 1 });
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
} finally {
|
||||
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` })
|
||||
await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` });
|
||||
}
|
||||
}
|
||||
|
||||
async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) {
|
||||
try {
|
||||
await executeCommand({ command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/` })
|
||||
await executeCommand({ sshCommand: true, shell: true, dockerId, command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'` })
|
||||
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({ sshCommand: true, dockerId, command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({
|
||||
command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
shell: true,
|
||||
dockerId,
|
||||
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
dockerId,
|
||||
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
await executeCommand({
|
||||
sshCommand: true,
|
||||
dockerId,
|
||||
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
async function copyLocalCertificates(id: string) {
|
||||
try {
|
||||
await executeCommand({ command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`, shell: true })
|
||||
await executeCommand({ command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({ command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` })
|
||||
await executeCommand({
|
||||
command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`,
|
||||
shell: true
|
||||
});
|
||||
await executeCommand({
|
||||
command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
await executeCommand({
|
||||
command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/`
|
||||
});
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupStorage() {
|
||||
const destinationDockers = await prisma.destinationDocker.findMany();
|
||||
let enginesDone = new Set()
|
||||
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)
|
||||
|
||||
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);
|
||||
}
|
||||
let lowDiskSpace = false;
|
||||
try {
|
||||
let stdout = null
|
||||
let stdout = null;
|
||||
if (!isDev) {
|
||||
const output = await executeCommand({ dockerId: destination.id, command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`, shell: true })
|
||||
const output = await executeCommand({
|
||||
dockerId: destination.id,
|
||||
command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`,
|
||||
shell: true
|
||||
});
|
||||
stdout = output.stdout;
|
||||
} else {
|
||||
const output = await executeCommand({
|
||||
command:
|
||||
`df -kPT /`
|
||||
command: `df -kPT /`
|
||||
});
|
||||
stdout = output.stdout;
|
||||
}
|
||||
@@ -531,7 +648,9 @@ async function cleanupStorage() {
|
||||
lowDiskSpace = true;
|
||||
}
|
||||
}
|
||||
} catch (error) { }
|
||||
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
|
||||
} catch (error) {}
|
||||
if (lowDiskSpace) {
|
||||
await cleanupDockerStorage(destination.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import {
|
||||
decryptApplication,
|
||||
isDev,
|
||||
pushToRegistry,
|
||||
executeCommand
|
||||
executeCommand,
|
||||
generateSecrets
|
||||
} from '../lib/common';
|
||||
import * as importers from '../lib/importers';
|
||||
import * as buildpacks from '../lib/buildPacks';
|
||||
@@ -140,33 +141,13 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
const envs = [`PORT='${port}'`];
|
||||
let envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(
|
||||
(s) => s.name === secret.name && s.isPRMRSecret
|
||||
);
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
});
|
||||
envs = [
|
||||
...envs,
|
||||
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||
];
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
|
||||
await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile);
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry;
|
||||
@@ -197,7 +178,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
container_name: applicationId,
|
||||
volumes,
|
||||
labels,
|
||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||
environment: envs,
|
||||
depends_on: [],
|
||||
expose: [port],
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
@@ -215,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) {
|
||||
@@ -405,7 +386,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
|
||||
try {
|
||||
dockerComposeConfiguration = JSON.parse(dockerComposeConfiguration);
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
let deployNeeded = true;
|
||||
let destinationType;
|
||||
|
||||
@@ -438,6 +419,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
githubAppId: gitSource.githubApp?.id,
|
||||
gitlabAppId: gitSource.gitlabApp?.id,
|
||||
customPort: gitSource.customPort,
|
||||
customUser: gitSource.customUser,
|
||||
gitCommitHash,
|
||||
configuration,
|
||||
repository,
|
||||
@@ -471,7 +453,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
|
||||
try {
|
||||
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
|
||||
if (!pullmergeRequestId) {
|
||||
if (configHash !== currentHash) {
|
||||
@@ -512,9 +494,8 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
try {
|
||||
await executeCommand({
|
||||
dockerId: destinationDocker.id,
|
||||
command: `docker ${
|
||||
location ? `--config ${location}` : ''
|
||||
} pull ${imageName}:${customTag}`
|
||||
command: `docker ${location ? `--config ${location}` : ''
|
||||
} pull ${imageName}:${customTag}`
|
||||
});
|
||||
imageFoundRemotely = true;
|
||||
} catch (error) {
|
||||
@@ -619,6 +600,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
}
|
||||
|
||||
if (buildPack === 'compose') {
|
||||
const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`;
|
||||
try {
|
||||
const { stdout: containers } = await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
@@ -648,7 +630,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({
|
||||
@@ -676,9 +658,8 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
try {
|
||||
const { stdout: containers } = await executeCommand({
|
||||
dockerId: destinationDockerId,
|
||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${
|
||||
pullmergeRequestId ? imageId : applicationId
|
||||
}' --format {{.ID}}`
|
||||
command: `docker ps -a --filter 'label=com.docker.compose.service=${pullmergeRequestId ? imageId : applicationId
|
||||
}' --format {{.ID}}`
|
||||
});
|
||||
if (containers) {
|
||||
const containerArray = containers.split('\n');
|
||||
@@ -698,37 +679,17 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
const envs = [`PORT='${port}'`];
|
||||
let envs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(
|
||||
(s) => s.name === secret.name && s.isPRMRSecret
|
||||
);
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
});
|
||||
envs = [
|
||||
...envs,
|
||||
...generateSecrets(secrets, pullmergeRequestId, false, port)
|
||||
];
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
if (dockerRegistry) {
|
||||
const { url, username, password } = dockerRegistry;
|
||||
await saveDockerRegistryCredentials({ url, username, password, workdir });
|
||||
}
|
||||
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
try {
|
||||
const composeVolumes = volumes.map((volume) => {
|
||||
return {
|
||||
@@ -744,7 +705,7 @@ import * as buildpacks from '../lib/buildPacks';
|
||||
image: imageFound,
|
||||
container_name: imageId,
|
||||
volumes,
|
||||
env_file: envFound ? [`${workdir}/.env`] : [],
|
||||
environment: envs,
|
||||
labels,
|
||||
depends_on: [],
|
||||
expose: [port],
|
||||
@@ -763,7 +724,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) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
decrypt,
|
||||
encrypt,
|
||||
executeCommand,
|
||||
generateSecrets,
|
||||
generateTimestamp,
|
||||
getDomain,
|
||||
isARM,
|
||||
@@ -340,8 +341,8 @@ export function setDefaultBaseImage(
|
||||
};
|
||||
if (nodeBased.includes(buildPack)) {
|
||||
if (deploymentType === 'static') {
|
||||
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
payload.baseImage = isARM() ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM()
|
||||
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
@@ -354,8 +355,8 @@ export function setDefaultBaseImage(
|
||||
}
|
||||
}
|
||||
if (staticApps.includes(buildPack)) {
|
||||
payload.baseImage = isARM(process.arch) ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
payload.baseImage = isARM() ? 'nginx:alpine' : 'webdevops/nginx:alpine';
|
||||
payload.baseImages = isARM()
|
||||
? staticVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: staticVersions;
|
||||
payload.baseBuildImage = 'node:lts';
|
||||
@@ -375,18 +376,18 @@ export function setDefaultBaseImage(
|
||||
payload.baseImage = 'denoland/deno:latest';
|
||||
}
|
||||
if (buildPack === 'php') {
|
||||
payload.baseImage = isARM(process.arch)
|
||||
payload.baseImage = isARM()
|
||||
? 'php:8.1-fpm-alpine'
|
||||
: 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
payload.baseImages = isARM()
|
||||
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: phpVersions;
|
||||
}
|
||||
if (buildPack === 'laravel') {
|
||||
payload.baseImage = isARM(process.arch)
|
||||
payload.baseImage = isARM()
|
||||
? 'php:8.1-fpm-alpine'
|
||||
: 'webdevops/php-apache:8.2-alpine';
|
||||
payload.baseImages = isARM(process.arch)
|
||||
payload.baseImages = isARM()
|
||||
? phpVersions.filter((version) => !version.value.includes('webdevops'))
|
||||
: phpVersions;
|
||||
payload.baseBuildImage = 'node:18';
|
||||
@@ -428,7 +429,12 @@ export const setDefaultConfiguration = async (data: any) => {
|
||||
startCommand = template?.startCommand || 'yarn start';
|
||||
if (!buildCommand && buildPack !== 'static' && buildPack !== 'laravel')
|
||||
buildCommand = template?.buildCommand || null;
|
||||
if (!publishDirectory) publishDirectory = template?.publishDirectory || null;
|
||||
if (!publishDirectory) {
|
||||
publishDirectory = template?.publishDirectory || null;
|
||||
} else {
|
||||
if (!publishDirectory.startsWith('/')) publishDirectory = `/${publishDirectory}`;
|
||||
if (publishDirectory.endsWith('/')) publishDirectory = publishDirectory.slice(0, -1);
|
||||
}
|
||||
if (baseDirectory) {
|
||||
if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`;
|
||||
if (baseDirectory.endsWith('/') && baseDirectory !== '/')
|
||||
@@ -653,7 +659,7 @@ export async function saveDockerRegistryCredentials({ url, username, password, w
|
||||
try {
|
||||
await fs.mkdir(`${workdir}/.docker`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// console.log(error);
|
||||
}
|
||||
const payload = JSON.stringify({
|
||||
auths: {
|
||||
@@ -701,9 +707,8 @@ export async function buildImage({
|
||||
buildId,
|
||||
applicationId,
|
||||
dockerId,
|
||||
command: `docker ${location ? `--config ${location}` : ''} build ${
|
||||
forceRebuild ? '--no-cache' : ''
|
||||
} --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`
|
||||
command: `docker ${location ? `--config ${location}` : ''} build ${forceRebuild ? '--no-cache' : ''
|
||||
} --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`
|
||||
});
|
||||
|
||||
const { status } = await prisma.build.findUnique({ where: { id: buildId } });
|
||||
@@ -787,21 +792,8 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret);
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
@@ -811,7 +803,6 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
if (installCommand) {
|
||||
Dockerfile.push(`RUN ${installCommand}`);
|
||||
}
|
||||
// Dockerfile.push(`ARG CACHEBUST=1`);
|
||||
Dockerfile.push(`RUN ${buildCommand}`);
|
||||
await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n'));
|
||||
await buildImage({ ...data, isCache: true });
|
||||
@@ -819,27 +810,13 @@ export async function buildCacheImageWithNode(data, imageForBuild) {
|
||||
|
||||
export async function buildCacheImageForLaravel(data, imageForBuild) {
|
||||
const { workdir, buildId, secrets, pullmergeRequestId } = data;
|
||||
|
||||
const Dockerfile: Array<string> = [];
|
||||
Dockerfile.push(`FROM ${imageForBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret);
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
Dockerfile.push(`COPY *.json *.mix.js /app/`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { defaultComposeConfiguration, executeCommand } from '../common';
|
||||
import { defaultComposeConfiguration, executeCommand, generateSecrets } from '../common';
|
||||
import { saveBuildLog } from './common';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
@@ -25,30 +25,13 @@ export default async function (data) {
|
||||
if (!dockerComposeYaml.services) {
|
||||
throw 'No Services found in docker-compose file.';
|
||||
}
|
||||
const envs = [];
|
||||
let envs = [];
|
||||
let buildEnvs = [];
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret);
|
||||
if (isSecretFound.length > 0) {
|
||||
envs.push(`${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
envs.push(`${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
|
||||
let envFound = false;
|
||||
try {
|
||||
envFound = !!(await fs.stat(`${workdir}/.env`));
|
||||
} catch (error) {
|
||||
//
|
||||
envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, null)];
|
||||
buildEnvs = [...buildEnvs, ...generateSecrets(secrets, pullmergeRequestId, true, null, true)];
|
||||
}
|
||||
|
||||
const composeVolumes = [];
|
||||
if (volumes.length > 0) {
|
||||
for (const volume of volumes) {
|
||||
@@ -62,7 +45,34 @@ export default async function (data) {
|
||||
let networks = {};
|
||||
for (let [key, value] of Object.entries(dockerComposeYaml.services)) {
|
||||
value['container_name'] = `${applicationId}-${key}`;
|
||||
value['env_file'] = envFound ? [`${workdir}/.env`] : [];
|
||||
|
||||
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) {
|
||||
@@ -85,7 +95,7 @@ export default async function (data) {
|
||||
value['volumes'].push(volume);
|
||||
}
|
||||
}
|
||||
if (dockerComposeConfiguration[key].port) {
|
||||
if (dockerComposeConfiguration[key]?.port) {
|
||||
value['expose'] = [dockerComposeConfiguration[key].port];
|
||||
}
|
||||
if (value['networks']?.length > 0) {
|
||||
@@ -94,8 +104,11 @@ export default async function (data) {
|
||||
name: network
|
||||
};
|
||||
});
|
||||
value['networks'] = [...(value['networks'] || ''), network];
|
||||
} else {
|
||||
value['networks'] = [network];
|
||||
}
|
||||
value['networks'] = [...(value['networks'] || ''), network];
|
||||
|
||||
dockerComposeYaml.services[key] = {
|
||||
...dockerComposeYaml.services[key],
|
||||
restart: defaultComposeConfiguration(network).restart,
|
||||
@@ -106,13 +119,14 @@ export default async function (data) {
|
||||
dockerComposeYaml['volumes'] = { ...composeVolumes };
|
||||
}
|
||||
dockerComposeYaml['networks'] = Object.assign({ ...networks }, { [network]: { external: true } });
|
||||
|
||||
await fs.writeFile(fileYaml, yaml.dump(dockerComposeYaml));
|
||||
await executeCommand({
|
||||
debug,
|
||||
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({
|
||||
@@ -120,7 +134,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 });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (depsFound) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
export default async function (data) {
|
||||
@@ -13,19 +14,12 @@ export default async function (data) {
|
||||
}
|
||||
});
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (
|
||||
(pullmergeRequestId && secret.isPRMRSecret) ||
|
||||
(!pullmergeRequestId && !secret.isPRMRSecret)
|
||||
) {
|
||||
Dockerfile.forEach((line, index) => {
|
||||
if (line.startsWith('FROM')) {
|
||||
Dockerfile.splice(index + 1, 0, `ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
});
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.forEach((line, index) => {
|
||||
if (line.startsWith('FROM')) {
|
||||
Dockerfile.splice(index + 1, 0, env);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
await fs.writeFile(`${data.workdir}${dockerFileLocation}`, Dockerfile.join('\n'));
|
||||
|
||||
@@ -8,7 +8,7 @@ const createDockerfile = async (data, imageforBuild): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${imageforBuild}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { promises as fs } from 'fs';
|
||||
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}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`ENV WEB_DOCUMENT_ROOT /app/public`);
|
||||
Dockerfile.push(`COPY --chown=application:application composer.* ./`);
|
||||
|
||||
@@ -12,7 +12,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (isPnpm) {
|
||||
Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7');
|
||||
}
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${baseDirectory || ''} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
|
||||
Dockerfile.push(`EXPOSE ${port}`);
|
||||
Dockerfile.push(`CMD ${startCommand}`);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
@@ -54,7 +42,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -20,21 +21,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter((s) => s.name === secret.name && s.isPRMRSecret);
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage, checkPnpm } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -24,21 +25,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (isPnpm) {
|
||||
@@ -54,7 +42,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
Dockerfile.push(`EXPOSE 80`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
@@ -13,21 +14,8 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -18,21 +19,8 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (pythonWSGI?.toLowerCase() === 'gunicorn') {
|
||||
|
||||
@@ -8,7 +8,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { promises as fs } from 'fs';
|
||||
import { generateSecrets } from '../common';
|
||||
import { buildCacheImageWithNode, buildImage } from './common';
|
||||
|
||||
const createDockerfile = async (data, image): Promise<void> => {
|
||||
@@ -25,25 +26,12 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
}
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
if (secrets.length > 0) {
|
||||
secrets.forEach((secret) => {
|
||||
if (secret.isBuildSecret) {
|
||||
if (pullmergeRequestId) {
|
||||
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
|
||||
if (isSecretFound.length > 0) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${isSecretFound[0].value}'`);
|
||||
} else {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
} else {
|
||||
if (!secret.isPRMRSecret) {
|
||||
Dockerfile.push(`ARG ${secret.name}='${secret.value}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => {
|
||||
Dockerfile.push(env);
|
||||
});
|
||||
}
|
||||
if (buildCommand) {
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
} else {
|
||||
Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const createDockerfile = async (data, image): Promise<void> => {
|
||||
Dockerfile.push(`FROM ${image}`);
|
||||
Dockerfile.push('WORKDIR /app');
|
||||
Dockerfile.push(`LABEL coolify.buildId=${buildId}`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
|
||||
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`);
|
||||
if (baseImage?.includes('nginx')) {
|
||||
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ import { saveBuildLog, saveDockerRegistryCredentials } from './buildPacks/common
|
||||
import { scheduler } from './scheduler';
|
||||
import type { ExecaChildProcess } from 'execa';
|
||||
|
||||
export const version = '3.12.2';
|
||||
export const version = '3.12.23';
|
||||
export const isDev = process.env.NODE_ENV === 'development';
|
||||
export const proxyPort = process.env.COOLIFY_PROXY_PORT;
|
||||
export const proxySecurePort = process.env.COOLIFY_PROXY_SECURE_PORT;
|
||||
export const sentryDSN =
|
||||
'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216';
|
||||
const algorithm = 'aes-256-ctr';
|
||||
@@ -515,7 +517,7 @@ export async function createRemoteEngineConfiguration(id: string) {
|
||||
await executeCommand({ command: `ssh-keygen -R ${Host}` });
|
||||
await executeCommand({ command: `ssh-keygen -R ${remoteIpAddress}` });
|
||||
await executeCommand({ command: `ssh-keygen -R localhost:${localPort}` });
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
|
||||
const found = config.find({ Host });
|
||||
const foundIp = config.find({ Host: remoteIpAddress });
|
||||
@@ -712,10 +714,12 @@ export async function startTraefikProxy(id: string): Promise<void> {
|
||||
-v coolify-traefik-letsencrypt:/etc/traefik/acme \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--network coolify-infra \
|
||||
-p "80:80" \
|
||||
-p "443:443" \
|
||||
-p ${proxyPort ? `${proxyPort}:80` : `80:80`} \
|
||||
-p ${proxySecurePort ? `${proxySecurePort}:443` : `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 \
|
||||
@@ -818,101 +822,101 @@ export function generatePassword({
|
||||
|
||||
type DatabaseConfiguration =
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MYSQL_DATABASE: string;
|
||||
MYSQL_PASSWORD: string;
|
||||
MYSQL_ROOT_USER: string;
|
||||
MYSQL_USER: string;
|
||||
MYSQL_ROOT_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGO_INITDB_ROOT_USERNAME?: string;
|
||||
MONGO_INITDB_ROOT_PASSWORD?: string;
|
||||
MONGODB_ROOT_USER?: string;
|
||||
MONGODB_ROOT_PASSWORD?: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MONGO_INITDB_ROOT_USERNAME?: string;
|
||||
MONGO_INITDB_ROOT_PASSWORD?: string;
|
||||
MONGODB_ROOT_USER?: string;
|
||||
MONGODB_ROOT_PASSWORD?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: string;
|
||||
MARIADB_ROOT_PASSWORD: string;
|
||||
MARIADB_USER: string;
|
||||
MARIADB_PASSWORD: string;
|
||||
MARIADB_DATABASE: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
MARIADB_ROOT_USER: string;
|
||||
MARIADB_ROOT_PASSWORD: string;
|
||||
MARIADB_USER: string;
|
||||
MARIADB_PASSWORD: string;
|
||||
MARIADB_DATABASE: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRES_PASSWORD?: string;
|
||||
POSTGRES_USER?: string;
|
||||
POSTGRES_DB?: string;
|
||||
POSTGRESQL_POSTGRES_PASSWORD?: string;
|
||||
POSTGRESQL_USERNAME?: string;
|
||||
POSTGRESQL_PASSWORD?: string;
|
||||
POSTGRESQL_DATABASE?: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
POSTGRES_PASSWORD?: string;
|
||||
POSTGRES_USER?: string;
|
||||
POSTGRES_DB?: string;
|
||||
POSTGRESQL_POSTGRES_PASSWORD?: string;
|
||||
POSTGRESQL_USERNAME?: string;
|
||||
POSTGRESQL_PASSWORD?: string;
|
||||
POSTGRESQL_DATABASE?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
REDIS_AOF_ENABLED: string;
|
||||
REDIS_PASSWORD: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
}
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
COUCHDB_PASSWORD: string;
|
||||
COUCHDB_USER: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
EDGEDB_SERVER_PASSWORD: string;
|
||||
EDGEDB_SERVER_USER: string;
|
||||
EDGEDB_SERVER_DATABASE: string;
|
||||
EDGEDB_SERVER_TLS_CERT_MODE: string;
|
||||
};
|
||||
};
|
||||
export function generateDatabaseConfiguration(database: any, arch: string): DatabaseConfiguration {
|
||||
volume: string;
|
||||
image: string;
|
||||
command?: string;
|
||||
ulimits: Record<string, unknown>;
|
||||
privatePort: number;
|
||||
environmentVariables: {
|
||||
EDGEDB_SERVER_PASSWORD: string;
|
||||
EDGEDB_SERVER_USER: string;
|
||||
EDGEDB_SERVER_DATABASE: string;
|
||||
EDGEDB_SERVER_TLS_CERT_MODE: string;
|
||||
};
|
||||
};
|
||||
export function generateDatabaseConfiguration(database: any): DatabaseConfiguration {
|
||||
const { id, dbUser, dbUserPassword, rootUser, rootUserPassword, defaultDatabase, version, type } =
|
||||
database;
|
||||
const baseImage = getDatabaseImage(type, arch);
|
||||
const baseImage = getDatabaseImage(type);
|
||||
if (type === 'mysql') {
|
||||
const configuration = {
|
||||
privatePort: 3306,
|
||||
@@ -927,7 +931,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/mysql/data`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
|
||||
}
|
||||
return configuration;
|
||||
@@ -945,7 +949,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/mariadb`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.volume = `${id}-${type}-data:/var/lib/mysql`;
|
||||
}
|
||||
return configuration;
|
||||
@@ -960,7 +964,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/mongodb`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.environmentVariables = {
|
||||
MONGO_INITDB_ROOT_USERNAME: rootUser,
|
||||
MONGO_INITDB_ROOT_PASSWORD: rootUserPassword
|
||||
@@ -981,7 +985,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/postgresql`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.volume = `${id}-${type}-data:/var/lib/postgresql`;
|
||||
configuration.environmentVariables = {
|
||||
POSTGRES_PASSWORD: dbUserPassword,
|
||||
@@ -1005,11 +1009,10 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/redis/data`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.volume = `${id}-${type}-data:/data`;
|
||||
configuration.command = `/usr/local/bin/redis-server --appendonly ${
|
||||
appendOnly ? 'yes' : 'no'
|
||||
} --requirepass ${dbUserPassword}`;
|
||||
configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no'
|
||||
} --requirepass ${dbUserPassword}`;
|
||||
}
|
||||
return configuration;
|
||||
} else if (type === 'couchdb') {
|
||||
@@ -1023,7 +1026,7 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
volume: `${id}-${type}-data:/bitnami/couchdb`,
|
||||
ulimits: {}
|
||||
};
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
configuration.volume = `${id}-${type}-data:/opt/couchdb/data`;
|
||||
}
|
||||
return configuration;
|
||||
@@ -1043,16 +1046,17 @@ export function generateDatabaseConfiguration(database: any, arch: string): Data
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
export function isARM(arch: string) {
|
||||
export function isARM() {
|
||||
const arch = process.arch;
|
||||
if (arch === 'arm' || arch === 'arm64' || arch === 'aarch' || arch === 'aarch64') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function getDatabaseImage(type: string, arch: string): string {
|
||||
export function getDatabaseImage(type: string): string {
|
||||
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
|
||||
if (found) {
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
return found.baseImageARM || found.baseImage;
|
||||
}
|
||||
return found.baseImage;
|
||||
@@ -1060,10 +1064,10 @@ export function getDatabaseImage(type: string, arch: string): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getDatabaseVersions(type: string, arch: string): string[] {
|
||||
export function getDatabaseVersions(type: string): string[] {
|
||||
const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type);
|
||||
if (found) {
|
||||
if (isARM(arch)) {
|
||||
if (isARM()) {
|
||||
return found.versionsARM || found.versions;
|
||||
}
|
||||
return found.versions;
|
||||
@@ -1093,12 +1097,12 @@ export type ComposeFileService = {
|
||||
command?: string;
|
||||
ports?: string[];
|
||||
build?:
|
||||
| {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
}
|
||||
| string;
|
||||
| {
|
||||
context: string;
|
||||
dockerfile: string;
|
||||
args?: Record<string, unknown>;
|
||||
}
|
||||
| string;
|
||||
deploy?: {
|
||||
restart_policy?: {
|
||||
condition?: string;
|
||||
@@ -1169,7 +1173,7 @@ export const createDirectories = async ({
|
||||
let workdirFound = false;
|
||||
try {
|
||||
workdirFound = !!(await fs.stat(workdir));
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
if (workdirFound) {
|
||||
await executeCommand({ command: `rm -fr ${workdir}` });
|
||||
}
|
||||
@@ -1693,7 +1697,7 @@ export async function stopBuild(buildId, applicationId) {
|
||||
}
|
||||
}
|
||||
count++;
|
||||
} catch (error) {}
|
||||
} catch (error) { }
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
@@ -1712,70 +1716,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({
|
||||
await executeCommand({ dockerId, command: `docker image prune -af` });
|
||||
} catch (error) { }
|
||||
|
||||
// Prune coolify managed containers
|
||||
try {
|
||||
await executeCommand({
|
||||
dockerId,
|
||||
command: `docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs -r`,
|
||||
shell: true
|
||||
command: `docker container prune -f --filter "label=coolify.managed=true"`
|
||||
});
|
||||
} catch (error) { }
|
||||
|
||||
images = images.trim();
|
||||
if (images) {
|
||||
await executeCommand({
|
||||
dockerId,
|
||||
command: `docker rmi -f ${images}" -q | xargs -r`,
|
||||
shell: true
|
||||
});
|
||||
}
|
||||
} 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({
|
||||
dockerId,
|
||||
command: `docker images|grep -v "<none>"|grep -v REPOSITORY|awk '{print $1, $2}'`,
|
||||
shell: 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 (keepImage.length >= numberOfDockerImagesKeptLocally) {
|
||||
deleteImage.push(image2);
|
||||
} else {
|
||||
keepImage.push(image2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const image of deleteImage) {
|
||||
await executeCommand({ dockerId, command: `docker image rm -f ${image}` });
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -1875,3 +1833,66 @@ export async function pushToRegistry(
|
||||
command: pushCommand
|
||||
});
|
||||
}
|
||||
|
||||
function parseSecret(secret, isBuild) {
|
||||
if (secret.value.includes('$')) {
|
||||
secret.value = secret.value.replaceAll('$', '$$$$');
|
||||
}
|
||||
if (secret.value.includes('\\n')) {
|
||||
if (isBuild) {
|
||||
return `ARG ${secret.name}=${secret.value}`;
|
||||
} else {
|
||||
return `${secret.name}=${secret.value}`;
|
||||
}
|
||||
} else if (secret.value.includes(' ')) {
|
||||
if (isBuild) {
|
||||
return `ARG ${secret.name}='${secret.value}'`;
|
||||
} else {
|
||||
return `${secret.name}='${secret.value}'`;
|
||||
}
|
||||
} else {
|
||||
if (isBuild) {
|
||||
return `ARG ${secret.name}=${secret.value}`;
|
||||
} else {
|
||||
return `${secret.name}=${secret.value}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
export function generateSecrets(
|
||||
secrets: Array<any>,
|
||||
pullmergeRequestId: string,
|
||||
isBuild = false,
|
||||
port = null,
|
||||
compose = false
|
||||
): Array<string> {
|
||||
const envs = [];
|
||||
const isPRMRSecret = secrets.filter((s) => s.isPRMRSecret);
|
||||
const normalSecrets = secrets.filter((s) => !s.isPRMRSecret);
|
||||
if (pullmergeRequestId && isPRMRSecret.length > 0) {
|
||||
isPRMRSecret.forEach((secret) => {
|
||||
if (isBuild && !secret.isBuildSecret) {
|
||||
return;
|
||||
}
|
||||
const build = isBuild && secret.isBuildSecret;
|
||||
envs.push(parseSecret(secret, compose ? false : build));
|
||||
});
|
||||
}
|
||||
if (!pullmergeRequestId && normalSecrets.length > 0) {
|
||||
normalSecrets.forEach((secret) => {
|
||||
if (isBuild && !secret.isBuildSecret) {
|
||||
return;
|
||||
}
|
||||
const build = isBuild && secret.isBuildSecret;
|
||||
envs.push(parseSecret(secret, compose ? false : build));
|
||||
});
|
||||
}
|
||||
const portFound = envs.filter((env) => env.startsWith('PORT'));
|
||||
if (portFound.length === 0 && port && !isBuild) {
|
||||
envs.push(`PORT=${port}`);
|
||||
}
|
||||
const nodeEnv = envs.filter((env) => env.startsWith('NODE_ENV'));
|
||||
if (nodeEnv.length === 0 && !isBuild) {
|
||||
envs.push(`NODE_ENV=production`);
|
||||
}
|
||||
return envs;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ export default async function ({
|
||||
buildId,
|
||||
privateSshKey,
|
||||
customPort,
|
||||
forPublic
|
||||
forPublic,
|
||||
customUser,
|
||||
}: {
|
||||
applicationId: string;
|
||||
workdir: string;
|
||||
@@ -25,6 +26,7 @@ export default async function ({
|
||||
privateSshKey: string;
|
||||
customPort: number;
|
||||
forPublic: boolean;
|
||||
customUser: string;
|
||||
}): Promise<string> {
|
||||
const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||
if (!forPublic) {
|
||||
@@ -53,7 +55,7 @@ export default async function ({
|
||||
} else {
|
||||
await executeCommand({
|
||||
command:
|
||||
`git clone -q -b ${branch} git@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||
`git clone -q -b ${branch} ${customUser}@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export async function getTemplates() {
|
||||
try {
|
||||
let data = await open.readFile({ encoding: 'utf-8' });
|
||||
let jsonData = JSON.parse(data);
|
||||
if (isARM(process.arch)) {
|
||||
if (isARM()) {
|
||||
jsonData = jsonData.filter((d) => d.arch !== 'amd64');
|
||||
}
|
||||
return jsonData;
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
|
||||
const { id } = request.params;
|
||||
const teamId = request.user.teamId;
|
||||
const service = await getServiceFromDB({ id, teamId });
|
||||
const arm = isARM(service.arch);
|
||||
const arm = isARM();
|
||||
const { type, destinationDockerId, destinationDocker, persistentStorage, exposePort } =
|
||||
service;
|
||||
|
||||
@@ -50,24 +50,12 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
|
||||
const config = {};
|
||||
for (const s in template.services) {
|
||||
let newEnvironments = []
|
||||
if (arm) {
|
||||
if (template.services[s]?.environmentArm?.length > 0) {
|
||||
for (const environment of template.services[s].environmentArm) {
|
||||
let [env, ...value] = environment.split("=");
|
||||
value = value.join("=")
|
||||
if (!value.startsWith('$$secret') && value !== '') {
|
||||
newEnvironments.push(`${env}=${value}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (template.services[s]?.environment?.length > 0) {
|
||||
for (const environment of template.services[s].environment) {
|
||||
let [env, ...value] = environment.split("=");
|
||||
value = value.join("=")
|
||||
if (!value.startsWith('$$secret') && value !== '') {
|
||||
newEnvironments.push(`${env}=${value}`)
|
||||
}
|
||||
if (template.services[s]?.environment?.length > 0) {
|
||||
for (const environment of template.services[s].environment) {
|
||||
let [env, ...value] = environment.split("=");
|
||||
value = value.join("=")
|
||||
if (!value.startsWith('$$secret') && value !== '') {
|
||||
newEnvironments.push(`${env}=${value}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,12 +75,13 @@ export async function startService(request: FastifyRequest<ServiceStartStop>, fa
|
||||
}
|
||||
const customVolumes = await prisma.servicePersistentStorage.findMany({ where: { serviceId: id } })
|
||||
let volumes = new Set()
|
||||
if (arm) {
|
||||
template.services[s]?.volumesArm && template.services[s].volumesArm.length > 0 && template.services[s].volumesArm.forEach(v => volumes.add(v))
|
||||
if (arm && template.services[s]?.volumesArm?.length > 0) {
|
||||
template.services[s].volumesArm.forEach(v => volumes.add(v))
|
||||
} else {
|
||||
template.services[s]?.volumes && template.services[s].volumes.length > 0 && template.services[s].volumes.forEach(v => volumes.add(v))
|
||||
if (template.services[s]?.volumes?.length > 0) {
|
||||
template.services[s].volumes.forEach(v => volumes.add(v))
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround: old plausible analytics service wrong volume id name
|
||||
if (service.type === 'plausibleanalytics' && service.plausibleAnalytics?.id) {
|
||||
let temp = Array.from(volumes)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true',
|
||||
whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON,
|
||||
isRegistrationEnabled: settings.isRegistrationEnabled,
|
||||
isARM: isARM(process.arch)
|
||||
isARM: isARM()
|
||||
};
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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: {
|
||||
|
||||
@@ -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
@@ -22,11 +22,11 @@ export async function listSources(request: FastifyRequest) {
|
||||
export async function saveSource(request, reply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, htmlUrl, apiUrl, customPort, isSystemWide } = request.body
|
||||
let { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = request.body
|
||||
if (customPort) customPort = Number(customPort)
|
||||
await prisma.gitSource.update({
|
||||
where: { id },
|
||||
data: { name, htmlUrl, apiUrl, customPort, isSystemWide }
|
||||
data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide }
|
||||
});
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
@@ -48,6 +48,7 @@ export async function getSource(request: FastifyRequest<OnlyId>) {
|
||||
apiUrl: null,
|
||||
organization: null,
|
||||
customPort: 22,
|
||||
customUser: 'git',
|
||||
},
|
||||
settings
|
||||
}
|
||||
@@ -133,7 +134,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
try {
|
||||
const { id } = request.params
|
||||
const { teamId } = request.user
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort } =
|
||||
let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort, customUser } =
|
||||
request.body
|
||||
|
||||
if (oauthId) oauthId = Number(oauthId);
|
||||
@@ -142,7 +143,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
|
||||
if (id === 'new') {
|
||||
const newId = cuid()
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, customUser, teams: { connect: { id: teamId } } } });
|
||||
await prisma.gitlabApp.create({
|
||||
data: {
|
||||
teams: { connect: { id: teamId } },
|
||||
@@ -158,7 +159,7 @@ export async function saveGitLabSource(request: FastifyRequest<SaveGitLabSource>
|
||||
id: newId
|
||||
}
|
||||
} else {
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort } });
|
||||
await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort, customUser } });
|
||||
await prisma.gitlabApp.update({
|
||||
where: { id },
|
||||
data: {
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface SaveGitLabSource extends OnlyId {
|
||||
appSecret: string,
|
||||
groupName: string,
|
||||
customPort: number,
|
||||
customUser: string,
|
||||
}
|
||||
}
|
||||
export interface CheckGitLabOAuthId extends OnlyId {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,13 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
@@ -1,20 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
plugins: ['svelte3', '@typescript-eslint'],
|
||||
ignorePatterns: ['*.cjs'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
}
|
||||
};
|
||||
10
apps/client/.gitignore
vendored
10
apps/client/.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
@@ -1 +0,0 @@
|
||||
engine-strict=true
|
||||
@@ -1,13 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
# SvelteKit Static site
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"name": "client",
|
||||
"description": "Coolify's SvelteKit UI",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build && cp -Pr build/ ../../build/public",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.28.1",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.48",
|
||||
"@sveltejs/kit": "1.0.0-next.572",
|
||||
"@types/js-cookie": "3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||
"@typescript-eslint/parser": "5.44.0",
|
||||
"autoprefixer": "10.4.13",
|
||||
"eslint": "8.28.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-svelte3": "4.0.0",
|
||||
"postcss": "8.4.19",
|
||||
"postcss-load-config": "4.0.1",
|
||||
"prettier": "2.8.0",
|
||||
"prettier-plugin-svelte": "2.8.1",
|
||||
"svelte": "3.53.1",
|
||||
"svelte-check": "2.9.2",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tailwindcss": "3.2.4",
|
||||
"tslib": "2.4.1",
|
||||
"typescript": "4.9.3",
|
||||
"vite": "3.2.4"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@trpc/client": "10.1.0",
|
||||
"@trpc/server": "10.1.0",
|
||||
"cuid": "2.1.8",
|
||||
"daisyui": "2.41.0",
|
||||
"flowbite-svelte": "0.28.0",
|
||||
"js-cookie": "3.0.1",
|
||||
"server": "workspace:*",
|
||||
"superjson": "1.11.0"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
1793
apps/client/pnpm-lock.yaml
generated
1793
apps/client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||
tailwindcss(),
|
||||
//But others, like autoprefixer, need to run after,
|
||||
autoprefixer
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
9
apps/client/src/app.d.ts
vendored
9
apps/client/src/app.d.ts
vendored
@@ -1,9 +0,0 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div class="h-screen">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,284 +0,0 @@
|
||||
/* Write your global styles here, in PostCSS syntax */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-regular.woff2') format('woff2'),
|
||||
url('/poppins-v19-latin-ext_latin_devanagari-regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Poppins';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-500.woff2') format('woff2'),
|
||||
url('/poppins-v19-latin-ext_latin_devanagari-500.woff') format('woff');
|
||||
}
|
||||
|
||||
button {
|
||||
@apply text-sm !important;
|
||||
}
|
||||
html {
|
||||
@apply h-full min-h-full overflow-y-scroll;
|
||||
}
|
||||
body {
|
||||
@apply min-h-screen overflow-x-hidden bg-coolblack text-sm text-white scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200;
|
||||
}
|
||||
|
||||
input,
|
||||
.input {
|
||||
@apply h-12 w-96 rounded border border-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-200 disabled:bg-transparent disabled:bg-coolblack md:text-sm;
|
||||
}
|
||||
textarea {
|
||||
@apply min-w-[14rem] rounded border border-transparent bg-coolgray-200 p-2 text-xs tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:border disabled:border-dashed disabled:border-coolgray-200 disabled:bg-transparent md:text-sm;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer.disabled input {
|
||||
@apply placeholder:text-stone-600;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer input {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
#svelte .custom-select-wrapper .selectContainer {
|
||||
@apply h-12 rounded bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm;
|
||||
}
|
||||
|
||||
#svelte .listContainer {
|
||||
@apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-green-500 scrollbar-track-coolgray-200;
|
||||
}
|
||||
#svelte .selectedItem {
|
||||
@apply pl-2;
|
||||
}
|
||||
|
||||
#svelte .item.hover {
|
||||
@apply bg-coollabs text-white !important;
|
||||
}
|
||||
#svelte .item.active {
|
||||
@apply bg-coolgray-100 text-white;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply h-12 w-96 rounded bg-coolgray-200 p-2 text-xs font-bold tracking-tight text-white placeholder-stone-600 outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 disabled:text-stone-600 md:text-sm;
|
||||
}
|
||||
.custom-select-wrapper {
|
||||
--background: rgb(32 32 32);
|
||||
--inputColor: white;
|
||||
--multiItemPadding: 0;
|
||||
--multiSelectPadding: 0 0.5rem 0 0.5rem;
|
||||
--border: none;
|
||||
--placeholderColor: rgb(87 83 78);
|
||||
--listBackground: rgb(32 32 32);
|
||||
--itemColor: white;
|
||||
--itemHoverBG: rgb(107 22 237);
|
||||
--multiItemBG: rgb(32 32 32);
|
||||
--multiClearHoverBG: transparent;
|
||||
--multiClearHoverFill: rgb(239 68 68);
|
||||
--multiItemActiveBG: transparent;
|
||||
--multiClearBG: transparent;
|
||||
--clearSelectFocusColor: white;
|
||||
--clearSelectHoverColor: rgb(239 68 68);
|
||||
--multiItemBorderRadius: 0.25rem;
|
||||
--listShadow: none;
|
||||
}
|
||||
|
||||
label {
|
||||
@apply inline-block;
|
||||
}
|
||||
.btn {
|
||||
@apply text-white text-base min-w-fit no-animation;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply underline hover:text-white;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply p-2 px-4;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-lg lg:text-2xl font-bold;
|
||||
}
|
||||
.subtitle {
|
||||
@apply text-lg lg:text-xl font-bold text-indigo-300;
|
||||
}
|
||||
.label {
|
||||
@apply text-sm leading-6 font-semibold text-sky-500 dark:text-sky-400;
|
||||
}
|
||||
.card {
|
||||
@apply border bg-coolgray-100 border-coolgray-200 rounded p-2 space-y-2 sticky top-4 mb-2 items-center;
|
||||
}
|
||||
.icon-holder {
|
||||
overflow: hidden;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
margin-right: 8px;
|
||||
background: linear-gradient(0deg, #999, #ddd);
|
||||
}
|
||||
.instance-status-running {
|
||||
box-shadow: 1px 4px 5px #3df721;
|
||||
}
|
||||
.instance-status-stopped {
|
||||
box-shadow: 1px 4px 5px rgb(110, 191, 225);
|
||||
}
|
||||
.instance-status-error {
|
||||
box-shadow: 1px 4px 5px #fb00ff;
|
||||
}
|
||||
.instance-status-degraded {
|
||||
box-shadow: 1px 4px 5px #f7b121;
|
||||
}
|
||||
.badge-status-healthy,
|
||||
.badge-status-running {
|
||||
@apply text-green-500;
|
||||
}
|
||||
.badge-status-degraded {
|
||||
@apply text-green-500;
|
||||
}
|
||||
.badge-status-stopped {
|
||||
@apply text-sky-500;
|
||||
}
|
||||
.delete-button {
|
||||
@apply bg-red-600;
|
||||
}
|
||||
.delete-button:hover {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
/* Interchange menu position */
|
||||
.menu-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-left .menu-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-left .menu-bar > * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-top .menu-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-top .menu-bar > * {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.nav-main {
|
||||
@apply fixed top-0 left-0 min-h-screen w-16 min-w-[4rem] overflow-hidden border-r border-stone-800 bg-coolgray-200 scrollbar-w-1 scrollbar-thumb-coollabs scrollbar-track-coolgray-200 xl:overflow-visible;
|
||||
}
|
||||
|
||||
.nav-side {
|
||||
@apply absolute right-0 top-0 z-50 m-5 flex flex-wrap items-center justify-end space-x-2 bg-coolblack/40 text-white;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
@apply rounded p-1 transition duration-200;
|
||||
}
|
||||
|
||||
.icons {
|
||||
@apply rounded p-2 transition duration-200 hover:bg-coolgray-500 disabled:bg-coolblack disabled:text-coolgray-500 !important;
|
||||
}
|
||||
|
||||
.arrow-right-applications {
|
||||
@apply -ml-6 px-2 font-bold text-green-500;
|
||||
}
|
||||
|
||||
.border-gradient {
|
||||
border-bottom: 2px solid transparent;
|
||||
-o-border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image-slice: 1;
|
||||
}
|
||||
.border-gradient-full {
|
||||
border: 4px solid transparent;
|
||||
-o-border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image: linear-gradient(
|
||||
0.25turn,
|
||||
rgba(255, 249, 34),
|
||||
rgba(255, 0, 128),
|
||||
rgba(56, 2, 155, 0)
|
||||
);
|
||||
border-image-slice: 1;
|
||||
}
|
||||
|
||||
.box-selection {
|
||||
@apply min-w-[16rem] justify-center rounded border-transparent bg-coolgray-200 p-6 hover:border-transparent hover:bg-coolgray-400;
|
||||
}
|
||||
|
||||
.lds-heart {
|
||||
animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
@keyframes lds-heart {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
5% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
39% {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.sub-menu {
|
||||
@apply w-48 text-base font-bold hover:bg-coolgray-500 rounded p-2 hover:text-white text-stone-200 cursor-pointer;
|
||||
}
|
||||
|
||||
.sub-menu-active {
|
||||
@apply bg-coolgray-500 text-white;
|
||||
}
|
||||
|
||||
.table tbody td,
|
||||
.table tbody th,
|
||||
.table thead th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.table * {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
@apply flex flex-row z-10 w-full py-5 px-5;
|
||||
}
|
||||
.burger {
|
||||
@apply block m-[2px] h-[3px] w-5 rounded;
|
||||
}
|
||||
|
||||
.bg-coollabs-gradient {
|
||||
@apply bg-gradient-to-r from-purple-500 via-pink-500 to-red-500;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { addToast } from './store';
|
||||
|
||||
export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay));
|
||||
|
||||
export function errorNotification(error: any | { message: string }): void {
|
||||
if (error instanceof Error) {
|
||||
addToast({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
} else {
|
||||
addToast({
|
||||
message: error,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
export function getRndInteger(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
export let type = 'info';
|
||||
function success() {
|
||||
if (type === 'success') {
|
||||
return 'bg-dark lg:bg-primary';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:click={() => dispatch('click')}
|
||||
on:mouseover={() => dispatch('pause')}
|
||||
on:focus={() => dispatch('pause')}
|
||||
on:mouseout={() => dispatch('resume')}
|
||||
on:blur={() => dispatch('resume')}
|
||||
class={` flex flex-row justify-center alert shadow-lg text-white hover:scale-105 transition-all duration-100 cursor-pointer rounded ${success()}`}
|
||||
class:alert-error={type === 'error'}
|
||||
class:alert-info={type === 'info'}
|
||||
>
|
||||
{#if type === 'success'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current flex-shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{:else if type === 'error'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current flex-shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{:else if type === 'info'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/></svg
|
||||
>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Toast from './Toast.svelte';
|
||||
import { dismissToast, pauseToast, resumeToast, toasts } from '$lib/store';
|
||||
</script>
|
||||
|
||||
{#if $toasts.length > 0}
|
||||
<section>
|
||||
<article class="toast toast-top toast-center rounded-none w-2/3 lg:w-[20rem]" role="alert">
|
||||
{#each $toasts as toast (toast.id)}
|
||||
<Toast
|
||||
type={toast.type}
|
||||
on:resume={() => resumeToast(toast.id)}
|
||||
on:pause={() => pauseToast(toast.id)}
|
||||
on:click={() => dismissToast(toast.id)}>{@html toast.message}</Toast
|
||||
>
|
||||
{/each}
|
||||
</article>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
section {
|
||||
@apply fixed top-0 left-0 right-0 w-full flex flex-col mt-4 justify-center z-[1000];
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Tooltip } from 'flowbite-svelte';
|
||||
export let placement = 'bottom';
|
||||
export let color = 'bg-coollabs';
|
||||
export let triggeredBy = '#tooltip-default';
|
||||
</script>
|
||||
|
||||
<Tooltip {triggeredBy} {placement} arrow={false} defaultClass={color + ' font-thin text-xs text-left border-none p-2'} style="custom"
|
||||
><slot /></Tooltip
|
||||
>
|
||||
@@ -1,206 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { dev } from '$app/environment';
|
||||
import {
|
||||
addToast,
|
||||
appSession,
|
||||
features,
|
||||
updateLoading,
|
||||
isUpdateAvailable,
|
||||
latestVersion
|
||||
} from '$lib/store';
|
||||
import { asyncSleep, errorNotification } from '$lib/common';
|
||||
import { onMount } from 'svelte';
|
||||
import Tooltip from './Tooltip.svelte';
|
||||
|
||||
let updateStatus: any = {
|
||||
found: false,
|
||||
loading: false,
|
||||
success: null
|
||||
};
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
try {
|
||||
if (dev) {
|
||||
localStorage.setItem('lastVersion', $appSession.version);
|
||||
await asyncSleep(1000);
|
||||
updateStatus.loading = false;
|
||||
return window.location.reload();
|
||||
} else {
|
||||
localStorage.setItem('lastVersion', $appSession.version);
|
||||
// await post(`/update`, { type: 'update', latestVersion: $latestVersion });
|
||||
addToast({
|
||||
message: 'Update completed.<br><br>Waiting for the new version to start...',
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
let reachable = false;
|
||||
let tries = 0;
|
||||
do {
|
||||
await asyncSleep(4000);
|
||||
try {
|
||||
// await get(`/undead`);
|
||||
reachable = true;
|
||||
} catch (error) {
|
||||
reachable = false;
|
||||
}
|
||||
if (reachable) break;
|
||||
tries++;
|
||||
} while (!reachable || tries < 120);
|
||||
addToast({
|
||||
message: 'New version reachable. Reloading...',
|
||||
type: 'success'
|
||||
});
|
||||
updateStatus.loading = false;
|
||||
updateStatus.success = true;
|
||||
await asyncSleep(3000);
|
||||
return window.location.reload();
|
||||
}
|
||||
} catch (error) {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
onMount(async () => {
|
||||
if ($appSession.userId) {
|
||||
const overrideVersion = $features.latestVersion;
|
||||
if ($appSession.teamId === '0') {
|
||||
if ($updateLoading === true) return;
|
||||
try {
|
||||
$updateLoading = true;
|
||||
// const data = await get(`/update`);
|
||||
if (overrideVersion || data?.isUpdateAvailable) {
|
||||
$latestVersion = overrideVersion || data.latestVersion;
|
||||
if (overrideVersion) {
|
||||
$isUpdateAvailable = true;
|
||||
} else {
|
||||
$isUpdateAvailable = data.isUpdateAvailable;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
$updateLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="py-0 lg:py-2">
|
||||
{#if $appSession.teamId === '0'}
|
||||
{#if $isUpdateAvailable}
|
||||
<button
|
||||
id="update"
|
||||
disabled={updateStatus.success === false}
|
||||
on:click={update}
|
||||
class="icons bg-coollabs-gradient text-white duration-75 hover:scale-105 w-full"
|
||||
>
|
||||
{#if updateStatus.loading}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="lds-heart h-8 w-8 mx-auto"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572"
|
||||
/>
|
||||
</svg>
|
||||
{:else if updateStatus.success === null}
|
||||
<div class="flex items-center justify-center space-x-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="12" y1="8" x2="8" y2="12" />
|
||||
<line x1="12" y1="8" x2="12" y2="16" />
|
||||
<line x1="16" y1="12" x2="12" y2="8" />
|
||||
</svg>
|
||||
<span class="flex lg:hidden">Update available</span>
|
||||
</div>
|
||||
{:else if updateStatus.success}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-8 w-8"
|
||||
><path
|
||||
fill="#DD2E44"
|
||||
d="M11.626 7.488c-.112.112-.197.247-.268.395l-.008-.008L.134 33.141l.011.011c-.208.403.14 1.223.853 1.937.713.713 1.533 1.061 1.936.853l.01.01L28.21 24.735l-.008-.009c.147-.07.282-.155.395-.269 1.562-1.562-.971-6.627-5.656-11.313-4.687-4.686-9.752-7.218-11.315-5.656z"
|
||||
/><path
|
||||
fill="#EA596E"
|
||||
d="M13 12L.416 32.506l-.282.635.011.011c-.208.403.14 1.223.853 1.937.232.232.473.408.709.557L17 17l-4-5z"
|
||||
/><path
|
||||
fill="#A0041E"
|
||||
d="M23.012 13.066c4.67 4.672 7.263 9.652 5.789 11.124-1.473 1.474-6.453-1.118-11.126-5.788-4.671-4.672-7.263-9.654-5.79-11.127 1.474-1.473 6.454 1.119 11.127 5.791z"
|
||||
/><path
|
||||
fill="#AA8DD8"
|
||||
d="M18.59 13.609c-.199.161-.459.245-.734.215-.868-.094-1.598-.396-2.109-.873-.541-.505-.808-1.183-.735-1.862.128-1.192 1.324-2.286 3.363-2.066.793.085 1.147-.17 1.159-.292.014-.121-.277-.446-1.07-.532-.868-.094-1.598-.396-2.11-.873-.541-.505-.809-1.183-.735-1.862.13-1.192 1.325-2.286 3.362-2.065.578.062.883-.057 1.012-.134.103-.063.144-.123.148-.158.012-.121-.275-.446-1.07-.532-.549-.06-.947-.552-.886-1.102.059-.549.55-.946 1.101-.886 2.037.219 2.973 1.542 2.844 2.735-.13 1.194-1.325 2.286-3.364 2.067-.578-.063-.88.057-1.01.134-.103.062-.145.123-.149.157-.013.122.276.446 1.071.532 2.037.22 2.973 1.542 2.844 2.735-.129 1.192-1.324 2.286-3.362 2.065-.578-.062-.882.058-1.012.134-.104.064-.144.124-.148.158-.013.121.276.446 1.07.532.548.06.947.553.886 1.102-.028.274-.167.511-.366.671z"
|
||||
/><path
|
||||
fill="#77B255"
|
||||
d="M30.661 22.857c1.973-.557 3.334.323 3.658 1.478.324 1.154-.378 2.615-2.35 3.17-.77.216-1.001.584-.97.701.034.118.425.312 1.193.095 1.972-.555 3.333.325 3.657 1.479.326 1.155-.378 2.614-2.351 3.17-.769.216-1.001.585-.967.702.033.117.423.311 1.192.095.53-.149 1.084.16 1.233.691.148.532-.161 1.084-.693 1.234-1.971.555-3.333-.323-3.659-1.479-.324-1.154.379-2.613 2.353-3.169.77-.217 1.001-.584.967-.702-.032-.117-.422-.312-1.19-.096-1.974.556-3.334-.322-3.659-1.479-.325-1.154.378-2.613 2.351-3.17.768-.215.999-.585.967-.701-.034-.118-.423-.312-1.192-.096-.532.15-1.083-.16-1.233-.691-.149-.53.161-1.082.693-1.232z"
|
||||
/><path
|
||||
fill="#AA8DD8"
|
||||
d="M23.001 20.16c-.294 0-.584-.129-.782-.375-.345-.432-.274-1.061.156-1.406.218-.175 5.418-4.259 12.767-3.208.547.078.927.584.849 1.131-.078.546-.58.93-1.132.848-6.493-.922-11.187 2.754-11.233 2.791-.186.148-.406.219-.625.219z"
|
||||
/><path
|
||||
fill="#77B255"
|
||||
d="M5.754 16c-.095 0-.192-.014-.288-.042-.529-.159-.829-.716-.67-1.245 1.133-3.773 2.16-9.794.898-11.364-.141-.178-.354-.353-.842-.316-.938.072-.849 2.051-.848 2.071.042.551-.372 1.031-.922 1.072-.559.034-1.031-.372-1.072-.923-.103-1.379.326-4.035 2.692-4.214 1.056-.08 1.933.287 2.552 1.057 2.371 2.951-.036 11.506-.542 13.192-.13.433-.528.712-.958.712z"
|
||||
/><circle fill="#5C913B" cx="25.5" cy="9.5" r="1.5" /><circle
|
||||
fill="#9266CC"
|
||||
cx="2"
|
||||
cy="18"
|
||||
r="2"
|
||||
/><circle fill="#5C913B" cx="32.5" cy="19.5" r="1.5" /><circle
|
||||
fill="#5C913B"
|
||||
cx="23.5"
|
||||
cy="31.5"
|
||||
r="1.5"
|
||||
/><circle fill="#FFCC4D" cx="28" cy="4" r="2" /><circle
|
||||
fill="#FFCC4D"
|
||||
cx="32.5"
|
||||
cy="8.5"
|
||||
r="1.5"
|
||||
/><circle fill="#FFCC4D" cx="29.5" cy="12.5" r="1.5" /><circle
|
||||
fill="#FFCC4D"
|
||||
cx="7.5"
|
||||
cy="23.5"
|
||||
r="1.5"
|
||||
/></svg
|
||||
>
|
||||
{:else}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="h-9 w-8"
|
||||
><path
|
||||
fill="#FFCC4D"
|
||||
d="M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18"
|
||||
/><path
|
||||
fill="#664500"
|
||||
d="M22 27c0 2.763-1.791 3-4 3-2.21 0-4-.237-4-3 0-2.761 1.79-6 4-6 2.209 0 4 3.239 4 6zm8-12c-.124 0-.25-.023-.371-.072-5.229-2.091-7.372-5.241-7.461-5.374-.307-.46-.183-1.081.277-1.387.459-.306 1.077-.184 1.385.274.019.027 1.93 2.785 6.541 4.629.513.206.763.787.558 1.3-.157.392-.533.63-.929.63zM6 15c-.397 0-.772-.238-.929-.629-.205-.513.044-1.095.557-1.3 4.612-1.844 6.523-4.602 6.542-4.629.308-.456.929-.577 1.387-.27.457.308.581.925.275 1.383-.089.133-2.232 3.283-7.46 5.374C6.25 14.977 6.124 15 6 15z"
|
||||
/><path fill="#5DADEC" d="M24 16h4v19l-4-.046V16zM8 35l4-.046V16H8v19z" /><path
|
||||
fill="#664500"
|
||||
d="M14.999 18c-.15 0-.303-.034-.446-.105-3.512-1.756-7.07-.018-7.105 0-.495.249-1.095.046-1.342-.447-.247-.494-.047-1.095.447-1.342.182-.09 4.498-2.197 8.895 0 .494.247.694.848.447 1.342-.176.35-.529.552-.896.552zm14 0c-.15 0-.303-.034-.446-.105-3.513-1.756-7.07-.018-7.105 0-.494.248-1.094.047-1.342-.447-.247-.494-.047-1.095.447-1.342.182-.09 4.501-2.196 8.895 0 .494.247.694.848.447 1.342-.176.35-.529.552-.896.552z"
|
||||
/><ellipse fill="#5DADEC" cx="18" cy="34" rx="18" ry="2" /><ellipse
|
||||
fill="#E75A70"
|
||||
cx="18"
|
||||
cy="27"
|
||||
rx="3"
|
||||
ry="2"
|
||||
/></svg
|
||||
>
|
||||
{/if}
|
||||
</button>
|
||||
<Tooltip triggeredBy="#update" placement="right" color="bg-coolgray-200 text-white"
|
||||
>New Version Available!</Tooltip
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,17 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<line x1="4" y1="7" x2="20" y2="7" />
|
||||
<line x1="10" y1="11" x2="10" y2="17" />
|
||||
<line x1="14" y1="11" x2="14" y2="17" />
|
||||
<path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
|
||||
<path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 486 B |
@@ -1,10 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 262 B |
@@ -1,47 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Icons from '$lib/components/icons/applications';
|
||||
export let application: any;
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
{#if application.buildPack?.toLowerCase() === 'rust'}
|
||||
<Icons.Rust {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'node'}
|
||||
<Icons.Nodejs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'react'}
|
||||
<Icons.React {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'svelte'}
|
||||
<Icons.Svelte {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'vuejs'}
|
||||
<Icons.Vuejs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'php'}
|
||||
<Icons.Php {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'python'}
|
||||
<Icons.Python {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'static'}
|
||||
<Icons.Static {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nestjs'}
|
||||
<Icons.Nestjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nuxtjs'}
|
||||
<Icons.Nuxtjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'nextjs'}
|
||||
<Icons.Nextjs {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'gatsby'}
|
||||
<Icons.Gatsby {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'docker'}
|
||||
<Icons.Docker {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'astro'}
|
||||
<Icons.Astro {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'eleventy'}
|
||||
<Icons.Eleventy {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'deno'}
|
||||
<Icons.Deno {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'laravel'}
|
||||
<Icons.Laravel {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'heroku'}
|
||||
<Icons.Heroku {isAbsolute} />
|
||||
{:else if application.buildPack?.toLowerCase() === 'compose'}
|
||||
<Icons.Compose {isAbsolute} />
|
||||
{:else if application.simpleDockerfile}
|
||||
<Icons.Docker {isAbsolute} />
|
||||
{/if}
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-6 h-14 w-14' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 256 256"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
id="a"
|
||||
fill="#302649"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z"
|
||||
/>
|
||||
<path
|
||||
id="flame"
|
||||
fill="#EF661E"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,9 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<img
|
||||
alt="docker compose logo"
|
||||
class={isAbsolute ? 'w-16 h-16 absolute top-0 left-0 -m-8' : 'w-8 h-8 mx-auto'}
|
||||
src="/icons/compose.png"
|
||||
/>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
|
||||
<svg viewBox="0 0 128 128" class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12' : 'mx-auto w-10 h-10'}>
|
||||
<path d="M124.8 52.1c-4.3-2.5-10-2.8-14.8-1.4-.6-5.2-4-9.7-8-12.9l-1.6-1.3-1.4 1.6c-2.7 3.1-3.5 8.3-3.1 12.3.3 2.9 1.2 5.9 3 8.3-1.4.8-2.9 1.9-4.3 2.4-2.8 1-5.9 2-8.9 2H79V49H66V24H51v12H26v13H13v14H1.8l-.2 1.5c-.5 6.4.3 12.6 3 18.5l1.1 2.2.1.2c7.9 13.4 21.7 19 36.8 19 29.2 0 53.3-13.1 64.3-40.6 7.4.4 15-1.8 18.6-8.9l.9-1.8-1.6-1zM28 39h10v11H28V39zm13.1 44.2c0 1.7-1.4 3.1-3.1 3.1-1.7 0-3.1-1.4-3.1-3.1 0-1.7 1.4-3.1 3.1-3.1 1.7.1 3.1 1.4 3.1 3.1zM28 52h10v11H28V52zm-13 0h11v11H15V52zm27.7 50.2c-15.8-.1-24.3-5.4-31.3-12.4 2.1.1 4.1.2 5.9.2 1.6 0 3.2 0 4.7-.1 3.9-.2 7.3-.7 10.1-1.5 2.3 5.3 6.5 10.2 14 13.8h-3.4zM51 63H40V52h11v11zm0-13H40V39h11v11zm13 13H53V52h11v11zm0-13H53V39h11v11zm0-13H53V26h11v11zm13 26H66V52h11v11zM38.8 81.2c-.2-.1-.5-.2-.8-.2-1.2 0-2.2 1-2.2 2.2 0 1.2 1 2.2 2.2 2.2s2.2-1 2.2-2.2c0-.3-.1-.6-.2-.8-.2.3-.4.5-.8.5-.5 0-.9-.4-.9-.9.1-.4.3-.7.5-.8z" fill="#019BC6"></path>
|
||||
</svg>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<path fill="transparent" d="M18 0h92v128H18z" /><path
|
||||
d="M55.3 36.3h.4c1.1 0 1.5.9 1.5 2.3v41.8c0 1.8-.4 3-1.6 3l-4.8-.1c-1.2 0-1.6-1-1.6-3V45.5l-2.1.5c-1 0-1.5-.8-1.5-2.2v-3c0-1.2.4-2 1.4-2.2l8.3-2.2zm16 36.1l.1 3 .6 1.3.6.6.8.1h2.2c1 0 1.7.8 1.7 2v1.9c0 1.2-.6 2-1.8 2h-3.2l-2.3-.1c-.7-.2-1.4-.5-2.2-1a5.7 5.7 0 01-2-1.9c-.4-.8-.8-1.9-1-3.2-.4-1.4-.5-3-.5-4.8v-16h-1.5c-1.1 0-1.6-1-1.6-2.4v-1.7c0-1.4.5-2.3 1.6-2.3h1.5v-.1l.6-12.3c0-1.5.5-2.5 1.6-2.5h3.1c1.2 0 1.6 1 1.6 2.5v12.3h3.6c1.1 0 1.6 1 1.6 2.4V54c0 1.4-.5 2.3-1.6 2.3h-3.6v16.2zm9.4 15.7c0-2 .3-3.2 1.5-3.2.2 0 .4 0 1.4.4l1.1.3.2-.1.4-.7c.3-.6.4-1.6.4-3l-.6-3.3L79 52.7v-.9c0-1.2.3-2 1.2-2H84c.5 0 1 .3 1.3.6.3.4.5.9.6 1.6l3.4 18 2.6-17.8c.1-.8.3-1.3.6-1.7.3-.4.8-.6 1.3-.6h2.6c1 0 1.4.8 1.4 2l-.1.8L92 82.2c-.5 2.5-1 4.4-1.5 5.7a6.6 6.6 0 01-2 3c-.9.6-1.9.9-3.1.9h-.3c-2 0-3.3-.6-4.1-1.7-.3-.4-.4-1-.4-2zM31.3 38.8l8.2-2.1h.5c1 0 1.4.8 1.4 2.2v41.9c0 1.8-.4 2.9-1.6 2.9h-4.7c-1.2 0-1.6-1.1-1.6-3v-35l-2 .6c-1.2 0-1.6-.9-1.6-2.2v-3c0-1.2.4-2 1.4-2.3z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,13 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
fill="#64328B"
|
||||
d="M64,0C28.7,0,0,28.7,0,64v0c0,35.3,28.7,64,64,64s64-28.7,64-64v0C128,28.7,99.3,0,64,0z M13.2,64L64,114.8 C35.9,114.8,13.2,92.1,13.2,64z M75.4,113.5l-60.9-61C19.7,30,39.9,13.2,64,13.2c16.6,0,31.3,7.9,40.5,20.2l-7.5,7.2 C89.7,30.2,77.7,23.5,64,23.5c-17.6,0-32.5,11.2-38.1,26.8C33.1,57,75.4,98.8,78.1,102c12.7-4.7,22.3-15.5,25.4-28.9H81.9v-9.4 l33,0.2C114.8,88.2,98,108.4,75.4,113.5z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,15 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8 '}
|
||||
viewBox="0 0 72 80"
|
||||
>
|
||||
<path
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#430098"
|
||||
d="M64.8,0 L7.2,0 C3.224,0 0,3.224 0,7.2 L0,72.8 C0,76.776 3.224,80 7.2,80 L64.8,80 C68.776,80 72,76.776 72,72.8 L72,7.2 C72,3.224 68.776,0 64.8,0 Z M18,68 L18,52 L27,60 L18,68 Z M46,68 L46,44.11 C45.961,42.243 45.062,40 41,40 C32.866,40 23.742,44.091 23.651,44.132 L18,46.692 L18,12 L26,12 L26,34.711 C29.994,33.411 35.577,32 41,32 C45.945,32 48.905,33.944 50.517,35.575 C53.958,39.055 54.005,43.488 54.0002258,44 L54.0002258,68 L46,68 Z M48,25 L40,25 C43.144,20.875 45.118,16.534 46,12 L54,12 C53.46,16.544 51.618,20.9 48,25 Z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,14 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 50 52"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><title>Logomark</title><path
|
||||
d="M49.626 11.564a.809.809 0 0 1 .028.209v10.972a.8.8 0 0 1-.402.694l-9.209 5.302V39.25c0 .286-.152.55-.4.694L20.42 51.01c-.044.025-.092.041-.14.058-.018.006-.035.017-.054.022a.805.805 0 0 1-.41 0c-.022-.006-.042-.018-.063-.026-.044-.016-.09-.03-.132-.054L.402 39.944A.801.801 0 0 1 0 39.25V6.334c0-.072.01-.142.028-.21.006-.023.02-.044.028-.067.015-.042.029-.085.051-.124.015-.026.037-.047.055-.071.023-.032.044-.065.071-.093.023-.023.053-.04.079-.06.029-.024.055-.05.088-.069h.001l9.61-5.533a.802.802 0 0 1 .8 0l9.61 5.533h.002c.032.02.059.045.088.068.026.02.055.038.078.06.028.029.048.062.072.094.017.024.04.045.054.071.023.04.036.082.052.124.008.023.022.044.028.068a.809.809 0 0 1 .028.209v20.559l8.008-4.611v-10.51c0-.07.01-.141.028-.208.007-.024.02-.045.028-.068.016-.042.03-.085.052-.124.015-.026.037-.047.054-.071.024-.032.044-.065.072-.093.023-.023.052-.04.078-.06.03-.024.056-.05.088-.069h.001l9.611-5.533a.801.801 0 0 1 .8 0l9.61 5.533c.034.02.06.045.09.068.025.02.054.038.077.06.028.029.048.062.072.094.018.024.04.045.054.071.023.039.036.082.052.124.009.023.022.044.028.068zm-1.574 10.718v-9.124l-3.363 1.936-4.646 2.675v9.124l8.01-4.611zm-9.61 16.505v-9.13l-4.57 2.61-13.05 7.448v9.216l17.62-10.144zM1.602 7.719v31.068L19.22 48.93v-9.214l-9.204-5.209-.003-.002-.004-.002c-.031-.018-.057-.044-.086-.066-.025-.02-.054-.036-.076-.058l-.002-.003c-.026-.025-.044-.056-.066-.084-.02-.027-.044-.05-.06-.078l-.001-.003c-.018-.03-.029-.066-.042-.1-.013-.03-.03-.058-.038-.09v-.001c-.01-.038-.012-.078-.016-.117-.004-.03-.012-.06-.012-.09v-.002-21.481L4.965 9.654 1.602 7.72zm8.81-5.994L2.405 6.334l8.005 4.609 8.006-4.61-8.006-4.608zm4.164 28.764l4.645-2.674V7.719l-3.363 1.936-4.646 2.675v20.096l3.364-1.937zM39.243 7.164l-8.006 4.609 8.006 4.609 8.005-4.61-8.005-4.608zm-.801 10.605l-4.646-2.675-3.363-1.936v9.124l4.645 2.674 3.364 1.937v-9.124zM20.02 38.33l11.743-6.704 5.87-3.35-8-4.606-9.211 5.303-8.395 4.833 7.993 4.524z"
|
||||
fill="#FF2D20"
|
||||
fill-rule="evenodd"
|
||||
/></svg
|
||||
>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,14 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 fill-current text-blue-500'
|
||||
: 'mx-auto w-8 h-8 fill-current text-blue-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="M64 0C28.7 0 0 28.7 0 64s28.7 64 64 64c11.2 0 21.7-2.9 30.8-7.9L48.4 55.3v36.6h-6.8V41.8h6.8l50.5 75.8C116.4 106.2 128 86.5 128 64c0-35.3-28.7-64-64-64zm22.1 84.6l-7.5-11.3V41.8h7.5v42.8z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,18 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10 text-green-500' : 'mx-auto w-8 h-8 text-green-500'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
data-prefix="fab"
|
||||
data-icon="node-js"
|
||||
role="img"
|
||||
viewBox="0 0 448 512"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,24 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 400 400"
|
||||
>
|
||||
<g fill-rule="nonzero" transform="translate(0 50)" fill="none">
|
||||
<path
|
||||
d="M227.92099 83.45116l-13.6889 24.10141-46.8148-82.44693L23.7037 278.17052h97.3037c0 13.31084 10.61252 24.10142 23.70371 24.10142H23.70371c-8.46771 0-16.29145-4.59601-20.5246-12.05272-4.23315-7.4567-4.23272-16.64312.00114-24.0994L146.89383 13.05492c4.23415-7.45738 12.0596-12.05138 20.5284-12.05138 8.46878 0 16.29423 4.594 20.52839 12.05138l39.97037 70.39623z"
|
||||
fill="#00C58E"
|
||||
/>
|
||||
<path
|
||||
d="M331.6642 266.11981l-90.05432-158.56724-13.6889-24.10141-13.68888 24.10141-90.04445 158.56724c-4.23385 7.45629-4.23428 16.64271-.00113 24.09941 4.23314 7.4567 12.05689 12.05272 20.5246 12.05272h166.4c8.46946 0 16.29644-4.591 20.532-12.04837 4.23555-7.45736 4.23606-16.64592.00132-24.10376h.01976zM144.7111 278.17052L227.921 131.65399l83.19012 146.51653h-166.4z"
|
||||
fill="#FFF"
|
||||
/>
|
||||
<path
|
||||
d="M396.04938 290.22123c-4.23344 7.45557-12.05656 12.0507-20.52345 12.0507H311.1111c13.0912 0 23.7037-10.79057 23.7037-24.10141h40.66173L260.09877 74.98553l-18.4889 32.56704L227.921 83.45116l11.65432-20.51634c4.23416-7.45738 12.0596-12.05138 20.5284-12.05138 8.46879 0 16.29423 4.594 20.52839 12.05138l115.41728 203.185c4.23426 7.457 4.23426 16.6444 0 24.1014z"
|
||||
fill="#108775"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -1,15 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-6 h-14 w-14 text-white'
|
||||
: 'mx-auto w-8 h-8 text-white'}
|
||||
>
|
||||
<path
|
||||
fill="#6181B6"
|
||||
d="M64 33.039c-33.74 0-61.094 13.862-61.094 30.961s27.354 30.961 61.094 30.961 61.094-13.862 61.094-30.961-27.354-30.961-61.094-30.961zm-15.897 36.993c-1.458 1.364-3.077 1.927-4.86 2.507-1.783.581-4.052.461-6.811.461h-6.253l-1.733 10h-7.301l6.515-34h14.04c4.224 0 7.305 1.215 9.242 3.432 1.937 2.217 2.519 5.364 1.747 9.337-.319 1.637-.856 3.159-1.614 4.515-.759 1.357-1.75 2.624-2.972 3.748zm21.311 2.968l2.881-14.42c.328-1.688.208-2.942-.361-3.555-.57-.614-1.782-1.025-3.635-1.025h-5.79l-3.731 19h-7.244l6.515-33h7.244l-1.732 9h6.453c4.061 0 6.861.815 8.402 2.231s2.003 3.356 1.387 6.528l-3.031 15.241h-7.358zm40.259-11.178c-.318 1.637-.856 3.133-1.613 4.488-.758 1.357-1.748 2.598-2.971 3.722-1.458 1.364-3.078 1.927-4.86 2.507-1.782.581-4.053.461-6.812.461h-6.253l-1.732 10h-7.301l6.514-34h14.041c4.224 0 7.305 1.215 9.241 3.432 1.935 2.217 2.518 5.418 1.746 9.39zM95.919 54h-5.001l-2.727 14h4.442c2.942 0 5.136-.29 6.576-1.4 1.442-1.108 2.413-2.828 2.918-5.421.484-2.491.264-4.434-.66-5.458-.925-1.024-2.774-1.721-5.548-1.721zM38.934 54h-5.002l-2.727 14h4.441c2.943 0 5.136-.29 6.577-1.4 1.441-1.108 2.413-2.828 2.917-5.421.484-2.491.264-4.434-.66-5.458s-2.772-1.721-5.546-1.721z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,57 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-6 h-14 w-14' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<linearGradient
|
||||
id="a"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="70.252"
|
||||
y1="1237.476"
|
||||
x2="170.659"
|
||||
y2="1151.089"
|
||||
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
|
||||
><stop offset="0" stop-color="#5A9FD4" /><stop
|
||||
offset="1"
|
||||
stop-color="#306998"
|
||||
/></linearGradient
|
||||
><path
|
||||
fill="url(#a)"
|
||||
d="M63.391 1.988c-4.222.02-8.252.379-11.8 1.007-10.45 1.846-12.346 5.71-12.346 12.837v9.411h24.693v3.137h-33.961c-7.176 0-13.46 4.313-15.426 12.521-2.268 9.405-2.368 15.275 0 25.096 1.755 7.311 5.947 12.519 13.124 12.519h8.491v-11.282c0-8.151 7.051-15.34 15.426-15.34h24.665c6.866 0 12.346-5.654 12.346-12.548v-23.513c0-6.693-5.646-11.72-12.346-12.837-4.244-.706-8.645-1.027-12.866-1.008zm-13.354 7.569c2.55 0 4.634 2.117 4.634 4.721 0 2.593-2.083 4.69-4.634 4.69-2.56 0-4.633-2.097-4.633-4.69-.001-2.604 2.073-4.721 4.633-4.721z"
|
||||
/><linearGradient
|
||||
id="b"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="209.474"
|
||||
y1="1098.811"
|
||||
x2="173.62"
|
||||
y2="1149.537"
|
||||
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
|
||||
><stop offset="0" stop-color="#FFD43B" /><stop
|
||||
offset="1"
|
||||
stop-color="#FFE873"
|
||||
/></linearGradient
|
||||
><path
|
||||
fill="url(#b)"
|
||||
d="M91.682 28.38v10.966c0 8.5-7.208 15.655-15.426 15.655h-24.665c-6.756 0-12.346 5.783-12.346 12.549v23.515c0 6.691 5.818 10.628 12.346 12.547 7.816 2.297 15.312 2.713 24.665 0 6.216-1.801 12.346-5.423 12.346-12.547v-9.412h-24.664v-3.138h37.012c7.176 0 9.852-5.005 12.348-12.519 2.578-7.735 2.467-15.174 0-25.096-1.774-7.145-5.161-12.521-12.348-12.521h-9.268zm-13.873 59.547c2.561 0 4.634 2.097 4.634 4.692 0 2.602-2.074 4.719-4.634 4.719-2.55 0-4.633-2.117-4.633-4.719 0-2.595 2.083-4.692 4.633-4.692z"
|
||||
/><radialGradient
|
||||
id="c"
|
||||
cx="1825.678"
|
||||
cy="444.45"
|
||||
r="26.743"
|
||||
gradientTransform="matrix(0 -.24 -1.055 0 532.979 557.576)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
><stop offset="0" stop-color="#B8B8B8" stop-opacity=".498" /><stop
|
||||
offset="1"
|
||||
stop-color="#7F7F7F"
|
||||
stop-opacity="0"
|
||||
/></radialGradient
|
||||
><path
|
||||
opacity=".444"
|
||||
fill="url(#c)"
|
||||
enable-background="new"
|
||||
d="M97.309 119.597c0 3.543-14.816 6.416-33.091 6.416-18.276 0-33.092-2.873-33.092-6.416 0-3.544 14.815-6.417 33.092-6.417 18.275 0 33.091 2.872 33.091 6.417z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,16 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-blue-500'
|
||||
: 'mx-auto w-8 h-8 text-blue-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<g fill="#61DAFB"
|
||||
><circle cx="64" cy="64" r="11.4" /><path
|
||||
d="M107.3 45.2c-2.2-.8-4.5-1.6-6.9-2.3.6-2.4 1.1-4.8 1.5-7.1 2.1-13.2-.2-22.5-6.6-26.1-1.9-1.1-4-1.6-6.4-1.6-7 0-15.9 5.2-24.9 13.9-9-8.7-17.9-13.9-24.9-13.9-2.4 0-4.5.5-6.4 1.6-6.4 3.7-8.7 13-6.6 26.1.4 2.3.9 4.7 1.5 7.1-2.4.7-4.7 1.4-6.9 2.3-12.5 4.8-19.3 11.4-19.3 18.8s6.9 14 19.3 18.8c2.2.8 4.5 1.6 6.9 2.3-.6 2.4-1.1 4.8-1.5 7.1-2.1 13.2.2 22.5 6.6 26.1 1.9 1.1 4 1.6 6.4 1.6 7.1 0 16-5.2 24.9-13.9 9 8.7 17.9 13.9 24.9 13.9 2.4 0 4.5-.5 6.4-1.6 6.4-3.7 8.7-13 6.6-26.1-.4-2.3-.9-4.7-1.5-7.1 2.4-.7 4.7-1.4 6.9-2.3 12.5-4.8 19.3-11.4 19.3-18.8s-6.8-14-19.3-18.8zm-14.8-30.5c4.1 2.4 5.5 9.8 3.8 20.3-.3 2.1-.8 4.3-1.4 6.6-5.2-1.2-10.7-2-16.5-2.5-3.4-4.8-6.9-9.1-10.4-13 7.4-7.3 14.9-12.3 21-12.3 1.3 0 2.5.3 3.5.9zm-11.2 59.3c-1.8 3.2-3.9 6.4-6.1 9.6-3.7.3-7.4.4-11.2.4-3.9 0-7.6-.1-11.2-.4-2.2-3.2-4.2-6.4-6-9.6-1.9-3.3-3.7-6.7-5.3-10 1.6-3.3 3.4-6.7 5.3-10 1.8-3.2 3.9-6.4 6.1-9.6 3.7-.3 7.4-.4 11.2-.4 3.9 0 7.6.1 11.2.4 2.2 3.2 4.2 6.4 6 9.6 1.9 3.3 3.7 6.7 5.3 10-1.7 3.3-3.4 6.6-5.3 10zm8.3-3.3c1.5 3.5 2.7 6.9 3.8 10.3-3.4.8-7 1.4-10.8 1.9 1.2-1.9 2.5-3.9 3.6-6 1.2-2.1 2.3-4.2 3.4-6.2zm-25.6 27.1c-2.4-2.6-4.7-5.4-6.9-8.3 2.3.1 4.6.2 6.9.2 2.3 0 4.6-.1 6.9-.2-2.2 2.9-4.5 5.7-6.9 8.3zm-18.6-15c-3.8-.5-7.4-1.1-10.8-1.9 1.1-3.3 2.3-6.8 3.8-10.3 1.1 2 2.2 4.1 3.4 6.1 1.2 2.2 2.4 4.1 3.6 6.1zm-7-25.5c-1.5-3.5-2.7-6.9-3.8-10.3 3.4-.8 7-1.4 10.8-1.9-1.2 1.9-2.5 3.9-3.6 6-1.2 2.1-2.3 4.2-3.4 6.2zm25.6-27.1c2.4 2.6 4.7 5.4 6.9 8.3-2.3-.1-4.6-.2-6.9-.2-2.3 0-4.6.1-6.9.2 2.2-2.9 4.5-5.7 6.9-8.3zm22.2 21l-3.6-6c3.8.5 7.4 1.1 10.8 1.9-1.1 3.3-2.3 6.8-3.8 10.3-1.1-2.1-2.2-4.2-3.4-6.2zm-54.5-16.2c-1.7-10.5-.3-17.9 3.8-20.3 1-.6 2.2-.9 3.5-.9 6 0 13.5 4.9 21 12.3-3.5 3.8-7 8.2-10.4 13-5.8.5-11.3 1.4-16.5 2.5-.6-2.3-1-4.5-1.4-6.6zm-24.7 29c0-4.7 5.7-9.7 15.7-13.4 2-.8 4.2-1.5 6.4-2.1 1.6 5 3.6 10.3 6 15.6-2.4 5.3-4.5 10.5-6 15.5-13.8-4-22.1-10-22.1-15.6zm28.5 49.3c-4.1-2.4-5.5-9.8-3.8-20.3.3-2.1.8-4.3 1.4-6.6 5.2 1.2 10.7 2 16.5 2.5 3.4 4.8 6.9 9.1 10.4 13-7.4 7.3-14.9 12.3-21 12.3-1.3 0-2.5-.3-3.5-.9zm60.8-20.3c1.7 10.5.3 17.9-3.8 20.3-1 .6-2.2.9-3.5.9-6 0-13.5-4.9-21-12.3 3.5-3.8 7-8.2 10.4-13 5.8-.5 11.3-1.4 16.5-2.5.6 2.3 1 4.5 1.4 6.6zm9-15.6c-2 .8-4.2 1.5-6.4 2.1-1.6-5-3.6-10.3-6-15.6 2.4-5.3 4.5-10.5 6-15.5 13.8 4 22.1 10 22.1 15.6 0 4.7-5.8 9.7-15.7 13.4z"
|
||||
/></g
|
||||
>
|
||||
</svg>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,34 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-white'
|
||||
: 'mx-auto w-8 h-8 text-white'}
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><g clip-path="url(#HTML5_Clip0_4)"
|
||||
><path
|
||||
d="M30.216 0L27.6454 28.7967L16.0907 32L4.56783 28.8012L2 0H30.216Z"
|
||||
fill="#E44D26"
|
||||
/><path
|
||||
d="M16.108 29.5515L25.4447 26.963L27.6415 2.35497H16.108V29.5515Z"
|
||||
fill="#F16529"
|
||||
/><path
|
||||
d="M11.1109 9.4197H16.108V5.88731H7.25053L7.33509 6.83499L8.20327 16.5692H16.108V13.0369H11.4338L11.1109 9.4197Z"
|
||||
fill="#EBEBEB"
|
||||
/><path
|
||||
d="M11.907 18.3354H8.36111L8.856 23.8818L16.0917 25.8904L16.108 25.8859V22.2108L16.0925 22.2149L12.1585 21.1527L11.907 18.3354Z"
|
||||
fill="#EBEBEB"
|
||||
/><path
|
||||
d="M16.0958 16.5692H20.4455L20.0354 21.1504L16.0958 22.2138V25.8887L23.3373 23.8817L23.3904 23.285L24.2205 13.9855L24.3067 13.0369H16.0958V16.5692Z"
|
||||
fill="white"
|
||||
/><path
|
||||
d="M16.0958 9.41105V9.41969H24.6281L24.6989 8.62572L24.8599 6.83499L24.9444 5.88731H16.0958V9.41105Z"
|
||||
fill="white"
|
||||
/></g
|
||||
><defs><clipPath id="HTML5_Clip0_4"><rect width="32" height="32" fill="white" /></clipPath></defs
|
||||
></svg
|
||||
>
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 98.1 118"
|
||||
style="enable-background:new 0 0 98.1 118;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
fill="#FF3E00"
|
||||
d="M91.8,15.6C80.9-0.1,59.2-4.7,43.6,5.2L16.1,22.8C8.6,27.5,3.4,35.2,1.9,43.9c-1.3,7.3-0.2,14.8,3.3,21.3 c-2.4,3.6-4,7.6-4.7,11.8c-1.6,8.9,0.5,18.1,5.7,25.4c11,15.7,32.6,20.3,48.2,10.4l27.5-17.5c7.5-4.7,12.7-12.4,14.2-21.1 c1.3-7.3,0.2-14.8-3.3-21.3c2.4-3.6,4-7.6,4.7-11.8C99.2,32.1,97.1,22.9,91.8,15.6"
|
||||
/>
|
||||
<path
|
||||
fill="#FFFFFF"
|
||||
d="M40.9,103.9c-8.9,2.3-18.2-1.2-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3c0.2-0.9,0.4-1.7,0.6-2.6l0.5-1.6l1.4,1 c3.3,2.4,6.9,4.2,10.8,5.4l1,0.3l-0.1,1c-0.1,1.4,0.3,2.9,1.1,4.1c1.6,2.3,4.4,3.4,7.1,2.7c0.6-0.2,1.2-0.4,1.7-0.7L65.5,72 c1.4-0.9,2.3-2.2,2.6-3.8c0.3-1.6-0.1-3.3-1-4.6c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7l-10.5,6.7 c-1.7,1.1-3.6,1.9-5.6,2.4c-8.9,2.3-18.2-1.2-23.4-8.7c-3.1-4.4-4.4-9.9-3.4-15.3c0.9-5.2,4.1-9.9,8.6-12.7l27.5-17.5 c1.7-1.1,3.6-1.9,5.6-2.5c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.2,0.9-0.4,1.7-0.7,2.6l-0.5,1.6l-1.4-1 c-3.3-2.4-6.9-4.2-10.8-5.4l-1-0.3l0.1-1c0.1-1.4-0.3-2.9-1.1-4.1c-1.6-2.3-4.4-3.3-7.1-2.6c-0.6,0.2-1.2,0.4-1.7,0.7L32.4,46.1 c-1.4,0.9-2.3,2.2-2.6,3.8s0.1,3.3,1,4.6c1.6,2.3,4.4,3.3,7.1,2.6c0.6-0.2,1.2-0.4,1.7-0.7l10.5-6.7c1.7-1.1,3.6-1.9,5.6-2.5 c8.9-2.3,18.2,1.2,23.4,8.7c3.2,4.4,4.4,9.9,3.5,15.3c-0.9,5.2-4.1,9.9-8.6,12.7l-27.5,17.5C44.8,102.5,42.9,103.3,40.9,103.9"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,21 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = true;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-4 h-10 w-10 text-green-500'
|
||||
: 'mx-auto w-8 h-8 text-green-500'}
|
||||
viewBox="0 0 128 128"
|
||||
>
|
||||
<path
|
||||
d="m-2.3125e-8 8.9337 49.854 0.1586 14.167 24.47 14.432-24.47 49.547-0.1577-63.834 110.14zm126.98 0.6374-24.36 0.0207-38.476 66.052-38.453-66.052-24.749-0.0194 63.211 107.89zm-25.149-0.008-22.745 0.16758l-15.053 24.647-14.817-24.647-22.794-0.1679 37.731 64.476zM25.997 9.3929l23.002 0.0087M25.997 9.3929l23.002 0.0087"
|
||||
fill="none"
|
||||
/><path
|
||||
d="m25.997 9.3929 23.002 0.0087l15.036 24.958 14.983-24.956 22.982-0.0057-37.85 65.655z"
|
||||
fill="#35495e"
|
||||
/><path
|
||||
d="m0.91068 9.5686 25.066-0.1711 38.151 65.658 37.852-65.654 25.11 0.0263-62.966 108.06z"
|
||||
fill="#41b883"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,20 +0,0 @@
|
||||
//@ts-nocheck
|
||||
export { default as Rust } from './Rust.svelte';
|
||||
export { default as Nodejs } from './Nodejs.svelte';
|
||||
export { default as React } from './React.svelte';
|
||||
export { default as Svelte } from './Svelte.svelte';
|
||||
export { default as Vuejs } from './Vuejs.svelte';
|
||||
export { default as Php } from './PHP.svelte';
|
||||
export { default as Python } from './Python.svelte';
|
||||
export { default as Static } from './Static.svelte';
|
||||
export { default as Nestjs } from './Nestjs.svelte';
|
||||
export { default as Nuxtjs } from './Nuxtjs.svelte';
|
||||
export { default as Nextjs } from './Nextjs.svelte';
|
||||
export { default as Gatsby } from './Gatsby.svelte';
|
||||
export { default as Docker } from './Docker.svelte';
|
||||
export { default as Astro } from './Astro.svelte';
|
||||
export { default as Eleventy } from './Eleventy.svelte';
|
||||
export { default as Deno } from './Deno.svelte';
|
||||
export { default as Laravel } from './Laravel.svelte';
|
||||
export { default as Heroku } from './Heroku.svelte';
|
||||
export { default as Compose } from './Compose.svelte';
|
||||
@@ -1,13 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
viewBox="0 0 9 8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path d="m0 7h1v1h-1z" fill="#f00" /><path
|
||||
d="m0 0h1v7h-1zm2 0h1v8h-1zm2 0h1v8h-1zm2 0h1v8h-1zm2 3.25h1v1.5h-1z"
|
||||
fill="#fc0"
|
||||
/></svg
|
||||
>
|
||||
@@ -1,18 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute
|
||||
? 'absolute top-0 left-0 -m-5 h-10 w-10 fill-current text-red-500'
|
||||
: 'mx-auto w-8 h-8 text-red-500'}
|
||||
id="CouchDB"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 128 128"
|
||||
><g id="original"
|
||||
><path
|
||||
d="M101.4,77.2c0,5-2.7,7.5-7.6,7.7H33.9c-4.9,0-7.6-2.5-7.6-7.7,0-5,2.7-7.5,7.6-7.7H94.1C99,69.7,101.4,72.2,101.4,77.2ZM94.1,88.7H33.9c-4.9,0-7.6,2.4-7.6,7.7,0,5,2.7,7.4,7.6,7.7H94.1c4.9,0,7.6-2.5,7.6-7.7C101.4,91.1,99,88.7,94.1,88.7Zm18.6-42.1h0c-4.9,0-7.6,2.5-7.6,7.4V96.1c0,5,2.7,7.5,7.6,7.7h0c7.4-.2,11.3-7.7,11.3-22.9V62C124,51.8,120.1,46.8,112.7,46.6Zm-97.4,0h0C7.9,46.8,4,51.8,4,62V80.9c0,15.2,3.9,22.7,11.3,22.9h0c4.9,0,7.6-2.4,7.6-7.7V54.3C22.7,49.3,20.2,46.8,15.3,46.6Zm97.4-3.8c0-12.7-6.6-18.7-18.6-18.9H33.9c-12.2.2-18.6,6.5-18.6,18.9h0c7.4,0,11.3,4,11.3,11.5s3.9,11.4,11.3,11.4H90.4c7.3,0,11.3-3.9,11.3-11.4-.3-7.7,3.9-11.2,11-11.5Z"
|
||||
/></g
|
||||
></svg
|
||||
>
|
||||
@@ -1,21 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Icons from '$lib/components/icons/databases';
|
||||
export let type: any;
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
{#if type === 'mysql'}
|
||||
<Icons.MySQL {isAbsolute} />
|
||||
{:else if type === 'postgresql'}
|
||||
<Icons.PostgreSQL {isAbsolute} />
|
||||
{:else if type === 'mongodb'}
|
||||
<Icons.MongoDB {isAbsolute} />
|
||||
{:else if type === 'mariadb'}
|
||||
<Icons.MariaDB {isAbsolute} />
|
||||
{:else if type === 'redis'}
|
||||
<Icons.Redis {isAbsolute} />
|
||||
{:else if type === 'couchdb'}
|
||||
<Icons.CouchDB {isAbsolute} />
|
||||
{:else if type === 'edgedb'}
|
||||
<Icons.EdgeDB {isAbsolute} />
|
||||
{/if}
|
||||
@@ -1,22 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-8 h-16 w-16' : 'mx-auto w-12 h-12'}
|
||||
width="88"
|
||||
fill="#1F8AED"
|
||||
height="101"
|
||||
viewBox="0 -15 88 101"
|
||||
><path
|
||||
class="pageNav_logoBar__2v4ah"
|
||||
style="transform-origin:center 35.5px"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M55.1436 71H58.1436V0H55.1436V71Z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M74.5362 35.3047C74.5362 41.3776 72.1013 42.4662 69.3799 42.4662H63.5935V28.1432H69.3799C72.1013 28.1432 74.5362 29.2318 74.5362 35.3047V35.3047ZM71.5862 35.3047C71.5862 31.0651 70.2971 30.8646 68.4352 30.8646H66.6305V39.7448H68.4352C70.2971 39.7448 71.5862 39.5443 71.5862 35.3047V35.3047ZM40.9348 42.4662V28.1432H50.0442V30.8646H43.9713V33.7865H48.5546V36.4792H43.9713V39.7448H50.0442V42.4662H40.9348ZM80.6092 36.1068V39.7448H83.13C84.7055 39.7448 85.1066 38.7135 85.1066 37.9401C85.1066 37.3385 84.8201 36.1068 82.6717 36.1068H80.6092ZM80.6092 30.8646V33.5859H82.6717C83.8462 33.5859 84.5337 33.0703 84.5337 32.2109C84.5337 31.3516 83.8462 30.8646 82.6717 30.8646H80.6092ZM77.5732 28.1432H83.4169C86.482 28.1432 87.3987 30.2917 87.3987 31.8385C87.3987 33.2708 86.482 34.3021 85.8518 34.5885C87.6851 35.4766 88.0002 37.2813 88.0002 38.1979C88.0002 39.401 87.3987 42.4662 83.4169 42.4662H77.5732V28.1432ZM23.4899 35.3047C23.4899 41.3776 21.055 42.4662 18.3337 42.4662H12.5472V28.1432H18.3337C21.055 28.1432 23.4899 29.2318 23.4899 35.3047V35.3047ZM32.4272 39.8594C33.974 39.8594 34.7761 39.3438 35.0626 39V37.4245H32.599V34.9609H37.4975V40.6615C37.0678 41.3203 34.7188 42.6094 32.5704 42.6094C29.047 42.6094 26.0678 41.2344 26.0678 35.1615C26.0678 29.0885 29.0756 28 31.797 28C36.0652 28 37.1251 30.2344 37.4688 32.2109L34.948 32.7839C34.8048 31.8672 34.0027 30.7214 32.1694 30.7214C30.3074 30.7214 29.0183 30.9219 29.0183 35.1615C29.0183 39.401 30.3647 39.8594 32.4272 39.8594V39.8594ZM20.539 35.3047C20.539 31.0651 19.2499 30.8646 17.3879 30.8646H15.5833V39.7448H17.3879C19.2499 39.7448 20.539 39.5443 20.539 35.3047V35.3047ZM0 42.4662V28.1432H9.10938V30.8646H3.03646V33.7865H7.61979V36.4792H3.03646V39.7448H9.10938V42.4662H0Z"
|
||||
/></svg
|
||||
>
|
||||
@@ -1,17 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 309.88 252.72"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-5 h-12 w-12 ' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M316,10.05a4.2,4.2,0,0,0-2.84-1c-2.84,0-6.5,1.92-8.46,3l-.79.4a26.81,26.81,0,0,1-10.57,2.66c-3.76.12-7,.34-11.22.77-25,2.58-36.15,21.74-46.89,40.27-5.84,10.08-11.88,20.5-20.16,28.57a55.71,55.71,0,0,1-5.46,4.63c-8.57,6.39-19.33,10.9-27.74,14.12-8.07,3.08-16.86,5.85-25.37,8.53-7.78,2.45-15.14,4.76-21.9,7.28-3.05,1.13-5.64,2-7.93,2.76-6.15,2-10.6,3.53-17.08,8-2.53,1.73-5.07,3.6-6.8,5a71.26,71.26,0,0,0-13.54,14.27A84.81,84.81,0,0,1,77.88,163c-1.36,1.34-3.8,2-7.43,2-4.27,0-9.43-.88-14.91-1.81s-11.46-2-16.46-2c-4.07,0-7.17.66-9.5,2,0,0-3.9,2.28-5.56,5.23l1.62.73a33.56,33.56,0,0,1,6.93,5,33.68,33.68,0,0,0,7.19,5.12A6.37,6.37,0,0,1,42,180.72c-.69,1-1.69,2.29-2.74,3.67-5.77,7.55-9.13,12.32-7.2,14.92a6,6,0,0,0,3,.68c12.59,0,19.34-3.27,27.9-7.41,2.47-1.2,5-2.44,8-3.7,5-2.17,10.38-5.63,16.08-9.29,7.55-4.85,15.36-9.87,22.92-12.3a62.3,62.3,0,0,1,19.23-2.7c8,0,16.42,1.07,24.54,2.11,6.06.78,12.32,1.58,18.47,2,2.39.14,4.6.21,6.76.21a78.48,78.48,0,0,0,8.61-.45l.68-.24c4.32-2.65,6.34-8.34,8.29-13.84,1.26-3.54,2.32-6.72,4-8.74a2.06,2.06,0,0,1,.33-.27.4.4,0,0,1,.49.08.25.25,0,0,1,0,.16c-1,21.51-9.67,35.16-18.42,47.3L177,199.14s8.18,0,12.84-1.8c17-5.08,29.84-16.28,39.18-34.14a144.39,144.39,0,0,0,6.16-14.09c.16-.4,1.64-1.14,1.49.93,0,.61-.08,1.29-.13,2h0c0,.42-.06.85-.08,1.28-.25,3-1,9.34-1,9.34l5.25-2.81c12.66-8,22.42-24.14,29.82-49.25,3.09-10.46,5.34-20.85,7.33-30,2.38-11,4.43-20.43,6.78-24.09,3.69-5.74,9.32-9.62,14.77-13.39.75-.51,1.49-1,2.22-1.54,6.86-4.81,13.67-10.36,15.16-20.71l0-.23C317.93,12.92,317,11,316,10.05Z"
|
||||
transform="translate(-7.45 -9.1)"
|
||||
/>
|
||||
</svg>
|
||||
@@ -1,90 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let isAbsolute = false;
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={isAbsolute ? 'absolute top-0 left-0 -m-4 h-10 w-10' : 'mx-auto w-8 h-8'}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#439934"
|
||||
d="M88.038 42.812c1.605 4.643 2.761 9.383 3.141 14.296.472 6.095.256 12.147-1.029 18.142-.035.165-.109.32-.164.48-.403.001-.814-.049-1.208.012-3.329.523-6.655 1.065-9.981 1.604-3.438.557-6.881 1.092-10.313 1.687-1.216.21-2.721-.041-3.212 1.641-.014.046-.154.054-.235.08l.166-10.051-.169-24.252 1.602-.275c2.62-.429 5.24-.864 7.862-1.281 3.129-.497 6.261-.98 9.392-1.465 1.381-.215 2.764-.412 4.148-.618z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#45A538"
|
||||
d="M61.729 110.054c-1.69-1.453-3.439-2.842-5.059-4.37-8.717-8.222-15.093-17.899-18.233-29.566-.865-3.211-1.442-6.474-1.627-9.792-.13-2.322-.318-4.665-.154-6.975.437-6.144 1.325-12.229 3.127-18.147l.099-.138c.175.233.427.439.516.702 1.759 5.18 3.505 10.364 5.242 15.551 5.458 16.3 10.909 32.604 16.376 48.9.107.318.384.579.583.866l-.87 2.969z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#46A037"
|
||||
d="M88.038 42.812c-1.384.206-2.768.403-4.149.616-3.131.485-6.263.968-9.392 1.465-2.622.417-5.242.852-7.862 1.281l-1.602.275-.012-1.045c-.053-.859-.144-1.717-.154-2.576-.069-5.478-.112-10.956-.18-16.434-.042-3.429-.105-6.857-.175-10.285-.043-2.13-.089-4.261-.185-6.388-.052-1.143-.236-2.28-.311-3.423-.042-.657.016-1.319.029-1.979.817 1.583 1.616 3.178 2.456 4.749 1.327 2.484 3.441 4.314 5.344 6.311 7.523 7.892 12.864 17.068 16.193 27.433z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#409433"
|
||||
d="M65.036 80.753c.081-.026.222-.034.235-.08.491-1.682 1.996-1.431 3.212-1.641 3.432-.594 6.875-1.13 10.313-1.687 3.326-.539 6.652-1.081 9.981-1.604.394-.062.805-.011 1.208-.012-.622 2.22-1.112 4.488-1.901 6.647-.896 2.449-1.98 4.839-3.131 7.182a49.142 49.142 0 01-6.353 9.763c-1.919 2.308-4.058 4.441-6.202 6.548-1.185 1.165-2.582 2.114-3.882 3.161l-.337-.23-1.214-1.038-1.256-2.753a41.402 41.402 0 01-1.394-9.838l.023-.561.171-2.426c.057-.828.133-1.655.168-2.485.129-2.982.241-5.964.359-8.946z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#4FAA41"
|
||||
d="M65.036 80.753c-.118 2.982-.23 5.964-.357 8.947-.035.83-.111 1.657-.168 2.485l-.765.289c-1.699-5.002-3.399-9.951-5.062-14.913-2.75-8.209-5.467-16.431-8.213-24.642a4498.887 4498.887 0 00-6.7-19.867c-.105-.31-.407-.552-.617-.826l4.896-9.002c.168.292.39.565.496.879a6167.476 6167.476 0 016.768 20.118c2.916 8.73 5.814 17.467 8.728 26.198.116.349.308.671.491 1.062l.67-.78-.167 10.052z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#4AA73C"
|
||||
d="M43.155 32.227c.21.274.511.516.617.826a4498.887 4498.887 0 016.7 19.867c2.746 8.211 5.463 16.433 8.213 24.642 1.662 4.961 3.362 9.911 5.062 14.913l.765-.289-.171 2.426-.155.559c-.266 2.656-.49 5.318-.814 7.968-.163 1.328-.509 2.632-.772 3.947-.198-.287-.476-.548-.583-.866-5.467-16.297-10.918-32.6-16.376-48.9a3888.972 3888.972 0 00-5.242-15.551c-.089-.263-.34-.469-.516-.702l3.272-8.84z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#57AE47"
|
||||
d="M65.202 70.702l-.67.78c-.183-.391-.375-.714-.491-1.062-2.913-8.731-5.812-17.468-8.728-26.198a6167.476 6167.476 0 00-6.768-20.118c-.105-.314-.327-.588-.496-.879l6.055-7.965c.191.255.463.482.562.769 1.681 4.921 3.347 9.848 5.003 14.778 1.547 4.604 3.071 9.215 4.636 13.813.105.308.47.526.714.786l.012 1.045c.058 8.082.115 16.167.171 24.251z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#60B24F"
|
||||
d="M65.021 45.404c-.244-.26-.609-.478-.714-.786-1.565-4.598-3.089-9.209-4.636-13.813-1.656-4.93-3.322-9.856-5.003-14.778-.099-.287-.371-.514-.562-.769 1.969-1.928 3.877-3.925 5.925-5.764 1.821-1.634 3.285-3.386 3.352-5.968.003-.107.059-.214.145-.514l.519 1.306c-.013.661-.072 1.322-.029 1.979.075 1.143.259 2.28.311 3.423.096 2.127.142 4.258.185 6.388.069 3.428.132 6.856.175 10.285.067 5.478.111 10.956.18 16.434.008.861.098 1.718.152 2.577z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#A9AA88"
|
||||
d="M62.598 107.085c.263-1.315.609-2.62.772-3.947.325-2.649.548-5.312.814-7.968l.066-.01.066.011a41.402 41.402 0 001.394 9.838c-.176.232-.425.439-.518.701-.727 2.05-1.412 4.116-2.143 6.166-.1.28-.378.498-.574.744l-.747-2.566.87-2.969z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#B6B598"
|
||||
d="M62.476 112.621c.196-.246.475-.464.574-.744.731-2.05 1.417-4.115 2.143-6.166.093-.262.341-.469.518-.701l1.255 2.754c-.248.352-.59.669-.728 1.061l-2.404 7.059c-.099.283-.437.483-.663.722l-.695-3.985z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#C2C1A7"
|
||||
d="M63.171 116.605c.227-.238.564-.439.663-.722l2.404-7.059c.137-.391.48-.709.728-1.061l1.215 1.037c-.587.58-.913 1.25-.717 2.097l-.369 1.208c-.168.207-.411.387-.494.624-.839 2.403-1.64 4.819-2.485 7.222-.107.305-.404.544-.614.812-.109-1.387-.22-2.771-.331-4.158z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#CECDB7"
|
||||
d="M63.503 120.763c.209-.269.506-.508.614-.812.845-2.402 1.646-4.818 2.485-7.222.083-.236.325-.417.494-.624l-.509 5.545c-.136.157-.333.294-.398.477-.575 1.614-1.117 3.24-1.694 4.854-.119.333-.347.627-.525.938-.158-.207-.441-.407-.454-.623-.051-.841-.016-1.688-.013-2.533z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#DBDAC7"
|
||||
d="M63.969 123.919c.178-.312.406-.606.525-.938.578-1.613 1.119-3.239 1.694-4.854.065-.183.263-.319.398-.477l.012 3.64-1.218 3.124-1.411-.495z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#EBE9DC"
|
||||
d="M65.38 124.415l1.218-3.124.251 3.696-1.469-.572z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#CECDB7"
|
||||
d="M67.464 110.898c-.196-.847.129-1.518.717-2.097l.337.23-1.054 1.867z"
|
||||
/><path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="#4FAA41"
|
||||
d="M64.316 95.172l-.066-.011-.066.01.155-.559-.023.56z"
|
||||
/>
|
||||
</svg>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user