mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-29 12:33:59 +00:00
Compare commits
79 Commits
v3.9.0-rc.
...
v3.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3a1bbc3d0 | ||
|
|
0078574ee6 | ||
|
|
7cfd313531 | ||
|
|
e7919e9a1b | ||
|
|
98073202e9 | ||
|
|
8dee345f85 | ||
|
|
9161882f33 | ||
|
|
eef313665b | ||
|
|
53e70fbfcb | ||
|
|
05a1721499 | ||
|
|
2f772080b8 | ||
|
|
a5548c080c | ||
|
|
7e0a1ecc80 | ||
|
|
3f2dcccc07 | ||
|
|
adc5965b32 | ||
|
|
6088f2e573 | ||
|
|
fc705746c0 | ||
|
|
8182359fe4 | ||
|
|
e7ae15162c | ||
|
|
12ca20432d | ||
|
|
8b7406e168 | ||
|
|
9d6317f782 | ||
|
|
d8bdb73140 | ||
|
|
476db15431 | ||
|
|
20ce356296 | ||
|
|
ea594dcbc6 | ||
|
|
021b9746a8 | ||
|
|
c4615ae557 | ||
|
|
95a5089bdc | ||
|
|
cef1fba281 | ||
|
|
5c7859a258 | ||
|
|
986cdae5b0 | ||
|
|
3b11e28d6c | ||
|
|
eba63e8e76 | ||
|
|
2fc65e3b42 | ||
|
|
7d504ab2bf | ||
|
|
216c7efd42 | ||
|
|
8c4149db16 | ||
|
|
20ac8f69ea | ||
|
|
1db3d7a6fb | ||
|
|
b1c1138cf8 | ||
|
|
00b1a4f174 | ||
|
|
86cc665b58 | ||
|
|
e26dd578ef | ||
|
|
f1f3217052 | ||
|
|
8f14fd89ef | ||
|
|
26e4d52a61 | ||
|
|
319c647147 | ||
|
|
f4cd93bd36 | ||
|
|
5a80bb1d2a | ||
|
|
1126dcacf5 | ||
|
|
bdf9a73d19 | ||
|
|
1f73b83a79 | ||
|
|
73bd62c51e | ||
|
|
9acd5c94e8 | ||
|
|
6e85eac14b | ||
|
|
936baf676e | ||
|
|
867f06d813 | ||
|
|
7f9f440789 | ||
|
|
5a15e64471 | ||
|
|
c9aecd51f3 | ||
|
|
6ca1d978d4 | ||
|
|
a18c73bd7c | ||
|
|
26d86cbcb5 | ||
|
|
b24a5d9aca | ||
|
|
5305bc1ceb | ||
|
|
a672f0f56c | ||
|
|
9ab5e13e8f | ||
|
|
a49171f8cc | ||
|
|
65d8dc412a | ||
|
|
8600400632 | ||
|
|
11d10bee12 | ||
|
|
dbd16e8285 | ||
|
|
eb26787079 | ||
|
|
b0a7b1eb3d | ||
|
|
f994092d7f | ||
|
|
946d8e5be5 | ||
|
|
6d7c2ae74a | ||
|
|
1ba71b0b1b |
@@ -20,7 +20,8 @@
|
|||||||
"svelte.svelte-vscode",
|
"svelte.svelte-vscode",
|
||||||
"ardenivanov.svelte-intellisense",
|
"ardenivanov.svelte-intellisense",
|
||||||
"Prisma.prisma",
|
"Prisma.prisma",
|
||||||
"bradlc.vscode-tailwindcss"
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"waderyan.gitblame"
|
||||||
],
|
],
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"forwardPorts": [3000, 3001],
|
"forwardPorts": [3000, 3001],
|
||||||
|
|||||||
62
.github/workflows/production-release.yml
vendored
62
.github/workflows/production-release.yml
vendored
@@ -5,11 +5,11 @@ on:
|
|||||||
types: [released]
|
types: [released]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
making-something-cool:
|
arm64-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -26,11 +26,59 @@ jobs:
|
|||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:latest,coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-arm64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-arm64,mode=max
|
||||||
|
amd64-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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: 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:${{steps.package-version.outputs.current-version}}-amd64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-amd64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-amd64,mode=max
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [amd64-build, arm64-build]
|
||||||
|
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: Get current package version
|
||||||
|
uses: martinbeentjes/npm-get-version-action@v1.2.3
|
||||||
|
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
|
||||||
|
docker manifest push coollabsio/coolify:${{steps.package-version.outputs.current-version}}
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
67
.github/workflows/release-candidate.yml
vendored
67
.github/workflows/release-candidate.yml
vendored
@@ -1,17 +1,16 @@
|
|||||||
name: release-candidate
|
name: release-candidate
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [prereleased]
|
types: [prereleased]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
making-something-cool:
|
arm64-making-something-cool:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: 'next'
|
ref: "next"
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -28,12 +27,64 @@ jobs:
|
|||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:${{github.event.release.name}}
|
tags: coollabsio/coolify:${{github.event.release.name}}-arm64
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-rc-arm64,mode=max
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
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}}
|
||||||
|
|
||||||
|
|||||||
64
.github/workflows/staging-release.yml
vendored
64
.github/workflows/staging-release.yml
vendored
@@ -6,11 +6,13 @@ on:
|
|||||||
- next
|
- next
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
staging-release:
|
arm64-making-something-cool:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: "next"
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -20,15 +22,65 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
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
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: coollabsio/coolify:next-arm64
|
||||||
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-arm64
|
||||||
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-arm64,mode=max
|
||||||
|
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:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: coollabsio/coolify:next
|
tags: coollabsio/coolify:next-amd64,coollabsio/coolify:next-test
|
||||||
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next
|
cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-amd64
|
||||||
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next,mode=max
|
cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max
|
||||||
|
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:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64
|
||||||
|
docker manifest push coollabsio/coolify:next
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
11
Dockerfile
11
Dockerfile
@@ -1,8 +1,11 @@
|
|||||||
|
ARG PNPM_VERSION=7.11.0
|
||||||
|
ARG NPM_VERSION=8.19.1
|
||||||
|
|
||||||
FROM node:18-slim as build
|
FROM node:18-slim as build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apt update && apt -y install curl
|
RUN apt update && apt -y install curl
|
||||||
RUN curl -sL https://unpkg.com/@pnpm/self-installer | node
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN pnpm install
|
RUN pnpm install
|
||||||
@@ -14,8 +17,10 @@ WORKDIR /app
|
|||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
RUN apt update && apt -y install git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3 && apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
|
RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3
|
||||||
RUN curl -sL https://unpkg.com/@pnpm/self-installer | node
|
RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
|
||||||
|
RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION}
|
||||||
|
RUN npm install -g npm@${PNPM_VERSION}
|
||||||
|
|
||||||
RUN mkdir -p ~/.docker/cli-plugins/
|
RUN mkdir -p ~/.docker/cli-plugins/
|
||||||
# https://download.docker.com/linux/static/stable/
|
# https://download.docker.com/linux/static/stable/
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@breejs/ts-worker": "2.0.0",
|
"@breejs/ts-worker": "2.0.0",
|
||||||
"@fastify/autoload": "5.2.0",
|
"@fastify/autoload": "5.3.1",
|
||||||
"@fastify/cookie": "8.1.0",
|
"@fastify/cookie": "8.1.0",
|
||||||
"@fastify/cors": "8.1.0",
|
"@fastify/cors": "8.1.0",
|
||||||
"@fastify/env": "4.1.0",
|
"@fastify/env": "4.1.0",
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"@fastify/static": "6.5.0",
|
"@fastify/static": "6.5.0",
|
||||||
"@iarna/toml": "2.2.5",
|
"@iarna/toml": "2.2.5",
|
||||||
"@ladjs/graceful": "3.0.2",
|
"@ladjs/graceful": "3.0.2",
|
||||||
"@prisma/client": "3.15.2",
|
"@prisma/client": "4.3.1",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"bree": "9.1.2",
|
"bree": "9.1.2",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"fastify": "4.5.3",
|
"fastify": "4.5.3",
|
||||||
"fastify-plugin": "4.2.1",
|
"fastify-plugin": "4.2.1",
|
||||||
"generate-password": "1.7.0",
|
"generate-password": "1.7.0",
|
||||||
"got": "12.3.1",
|
"got": "12.4.1",
|
||||||
"is-ip": "5.0.0",
|
"is-ip": "5.0.0",
|
||||||
"is-port-reachable": "4.0.0",
|
"is-port-reachable": "4.0.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
@@ -52,20 +52,20 @@
|
|||||||
"unique-names-generator": "4.7.1"
|
"unique-names-generator": "4.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.7.13",
|
"@types/node": "18.7.15",
|
||||||
"@types/node-os-utils": "1.3.0",
|
"@types/node-os-utils": "1.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "5.35.1",
|
"@typescript-eslint/eslint-plugin": "5.36.2",
|
||||||
"@typescript-eslint/parser": "5.35.1",
|
"@typescript-eslint/parser": "5.36.2",
|
||||||
"esbuild": "0.15.5",
|
"esbuild": "0.15.7",
|
||||||
"eslint": "8.23.0",
|
"eslint": "8.23.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"nodemon": "2.0.19",
|
"nodemon": "2.0.19",
|
||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
"prisma": "3.15.2",
|
"prisma": "4.3.1",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"tsconfig-paths": "4.1.0",
|
"tsconfig-paths": "4.1.0",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.8.2"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "node prisma/seed.js"
|
"seed": "node prisma/seed.js"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_ApplicationSettings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"debug" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"previews" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autodeploy" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isBot" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isPublicRepository" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDBBranching" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"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";
|
||||||
|
DROP TABLE "ApplicationSettings";
|
||||||
|
ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings";
|
||||||
|
CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to alter the column `time` on the `BuildLog` table. The data in that column could be lost. The data in that column will be cast from `Int` to `BigInt`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_BuildLog" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT,
|
||||||
|
"buildId" TEXT NOT NULL,
|
||||||
|
"line" TEXT NOT NULL,
|
||||||
|
"time" BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_BuildLog" ("applicationId", "buildId", "id", "line", "time") SELECT "applicationId", "buildId", "id", "line", "time" FROM "BuildLog";
|
||||||
|
DROP TABLE "BuildLog";
|
||||||
|
ALTER TABLE "new_BuildLog" RENAME TO "BuildLog";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ApplicationConnectedDatabase" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"applicationId" TEXT NOT NULL,
|
||||||
|
"databaseId" TEXT,
|
||||||
|
"hostedDatabaseType" TEXT,
|
||||||
|
"hostedDatabaseHost" TEXT,
|
||||||
|
"hostedDatabasePort" INTEGER,
|
||||||
|
"hostedDatabaseName" TEXT,
|
||||||
|
"hostedDatabaseUser" TEXT,
|
||||||
|
"hostedDatabasePassword" TEXT,
|
||||||
|
"hostedDatabaseDBName" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "ApplicationConnectedDatabase_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "ApplicationConnectedDatabase_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ApplicationConnectedDatabase_applicationId_key" ON "ApplicationConnectedDatabase"("applicationId");
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "isAPIDebuggingEnabled" BOOLEAN DEFAULT false;
|
||||||
@@ -11,6 +11,7 @@ datasource db {
|
|||||||
model Setting {
|
model Setting {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
fqdn String? @unique
|
fqdn String? @unique
|
||||||
|
isAPIDebuggingEnabled Boolean? @default(false)
|
||||||
isRegistrationEnabled Boolean @default(false)
|
isRegistrationEnabled Boolean @default(false)
|
||||||
dualCerts Boolean @default(false)
|
dualCerts Boolean @default(false)
|
||||||
minPort Int @default(9000)
|
minPort Int @default(9000)
|
||||||
@@ -117,6 +118,24 @@ model Application {
|
|||||||
settings ApplicationSettings?
|
settings ApplicationSettings?
|
||||||
secrets Secret[]
|
secrets Secret[]
|
||||||
teams Team[]
|
teams Team[]
|
||||||
|
connectedDatabase ApplicationConnectedDatabase?
|
||||||
|
}
|
||||||
|
|
||||||
|
model ApplicationConnectedDatabase {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
applicationId String @unique
|
||||||
|
databaseId String?
|
||||||
|
hostedDatabaseType String?
|
||||||
|
hostedDatabaseHost String?
|
||||||
|
hostedDatabasePort Int?
|
||||||
|
hostedDatabaseName String?
|
||||||
|
hostedDatabaseUser String?
|
||||||
|
hostedDatabasePassword String?
|
||||||
|
hostedDatabaseDBName String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
database Database? @relation(fields: [databaseId], references: [id])
|
||||||
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
model ApplicationSettings {
|
model ApplicationSettings {
|
||||||
@@ -128,6 +147,7 @@ model ApplicationSettings {
|
|||||||
autodeploy Boolean @default(true)
|
autodeploy Boolean @default(true)
|
||||||
isBot Boolean @default(false)
|
isBot Boolean @default(false)
|
||||||
isPublicRepository Boolean @default(false)
|
isPublicRepository Boolean @default(false)
|
||||||
|
isDBBranching Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
application Application @relation(fields: [applicationId], references: [id])
|
application Application @relation(fields: [applicationId], references: [id])
|
||||||
@@ -186,7 +206,7 @@ model BuildLog {
|
|||||||
applicationId String?
|
applicationId String?
|
||||||
buildId String
|
buildId String
|
||||||
line String
|
line String
|
||||||
time Int
|
time BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
model Build {
|
model Build {
|
||||||
@@ -291,22 +311,23 @@ model GitlabApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Database {
|
model Database {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
publicPort Int?
|
publicPort Int?
|
||||||
defaultDatabase String?
|
defaultDatabase String?
|
||||||
type String?
|
type String?
|
||||||
version String?
|
version String?
|
||||||
dbUser String?
|
dbUser String?
|
||||||
dbUserPassword String?
|
dbUserPassword String?
|
||||||
rootUser String?
|
rootUser String?
|
||||||
rootUserPassword String?
|
rootUserPassword String?
|
||||||
destinationDockerId String?
|
destinationDockerId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
|
||||||
settings DatabaseSettings?
|
settings DatabaseSettings?
|
||||||
teams Team[]
|
teams Team[]
|
||||||
|
applicationConnectedDatabase ApplicationConnectedDatabase[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model DatabaseSettings {
|
model DatabaseSettings {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ const algorithm = 'aes-256-ctr';
|
|||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
// Enable registration for the first user
|
// Enable registration for the first user
|
||||||
// Set initial HAProxy password
|
|
||||||
const settingsFound = await prisma.setting.findFirst({});
|
const settingsFound = await prisma.setting.findFirst({});
|
||||||
if (!settingsFound) {
|
if (!settingsFound) {
|
||||||
await prisma.setting.create({
|
await prisma.setting.create({
|
||||||
@@ -25,7 +24,8 @@ async function main() {
|
|||||||
isRegistrationEnabled: true,
|
isRegistrationEnabled: true,
|
||||||
proxyPassword: encrypt(generatePassword()),
|
proxyPassword: encrypt(generatePassword()),
|
||||||
proxyUser: cuid(),
|
proxyUser: cuid(),
|
||||||
arch: process.arch
|
arch: process.arch,
|
||||||
|
DNSServers: '1.1.1.1,8.8.8.8'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import env from '@fastify/env';
|
|||||||
import cookie from '@fastify/cookie';
|
import cookie from '@fastify/cookie';
|
||||||
import path, { join } from 'path';
|
import path, { join } from 'path';
|
||||||
import autoLoad from '@fastify/autoload';
|
import autoLoad from '@fastify/autoload';
|
||||||
import { asyncExecShell, isDev, listSettings, prisma, version } from './lib/common';
|
import { asyncExecShell, createRemoteEngineConfiguration, getDomain, isDev, listSettings, prisma, version } from './lib/common';
|
||||||
import { scheduler } from './lib/scheduler';
|
import { scheduler } from './lib/scheduler';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import Graceful from '@ladjs/graceful'
|
import Graceful from '@ladjs/graceful'
|
||||||
@@ -26,119 +26,143 @@ declare module 'fastify' {
|
|||||||
|
|
||||||
const port = isDev ? 3001 : 3000;
|
const port = isDev ? 3001 : 3000;
|
||||||
const host = '0.0.0.0';
|
const host = '0.0.0.0';
|
||||||
const fastify = Fastify({
|
prisma.setting.findFirst().then(async (settings) => {
|
||||||
logger: false,
|
const fastify = Fastify({
|
||||||
trustProxy: true
|
logger: settings?.isAPIDebuggingEnabled || false,
|
||||||
});
|
trustProxy: true
|
||||||
const schema = {
|
|
||||||
type: 'object',
|
|
||||||
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
|
||||||
properties: {
|
|
||||||
COOLIFY_APP_ID: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
COOLIFY_SECRET_KEY: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
COOLIFY_DATABASE_URL: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'file:../db/dev.db'
|
|
||||||
},
|
|
||||||
COOLIFY_SENTRY_DSN: {
|
|
||||||
type: 'string',
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
COOLIFY_IS_ON: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'docker'
|
|
||||||
},
|
|
||||||
COOLIFY_WHITE_LABELED: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'false'
|
|
||||||
},
|
|
||||||
COOLIFY_WHITE_LABELED_ICON: {
|
|
||||||
type: 'string',
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
COOLIFY_AUTO_UPDATE: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'false'
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
schema,
|
|
||||||
dotenv: true
|
|
||||||
};
|
|
||||||
fastify.register(env, options);
|
|
||||||
if (!isDev) {
|
|
||||||
fastify.register(serve, {
|
|
||||||
root: path.join(__dirname, './public'),
|
|
||||||
preCompressed: true
|
|
||||||
});
|
});
|
||||||
fastify.setNotFoundHandler(async function (request, reply) {
|
const schema = {
|
||||||
if (request.raw.url && request.raw.url.startsWith('/api')) {
|
type: 'object',
|
||||||
return reply.status(404).send({
|
required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'],
|
||||||
success: false
|
properties: {
|
||||||
});
|
COOLIFY_APP_ID: {
|
||||||
}
|
type: 'string',
|
||||||
return reply.status(200).sendFile('index.html');
|
},
|
||||||
});
|
COOLIFY_SECRET_KEY: {
|
||||||
}
|
type: 'string',
|
||||||
fastify.register(autoLoad, {
|
},
|
||||||
dir: join(__dirname, 'plugins')
|
COOLIFY_DATABASE_URL: {
|
||||||
});
|
type: 'string',
|
||||||
fastify.register(autoLoad, {
|
default: 'file:../db/dev.db'
|
||||||
dir: join(__dirname, 'routes')
|
},
|
||||||
});
|
COOLIFY_SENTRY_DSN: {
|
||||||
|
type: 'string',
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
COOLIFY_IS_ON: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'docker'
|
||||||
|
},
|
||||||
|
COOLIFY_WHITE_LABELED: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'false'
|
||||||
|
},
|
||||||
|
COOLIFY_WHITE_LABELED_ICON: {
|
||||||
|
type: 'string',
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
COOLIFY_AUTO_UPDATE: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'false'
|
||||||
|
},
|
||||||
|
|
||||||
fastify.register(cookie)
|
}
|
||||||
fastify.register(cors);
|
};
|
||||||
fastify.listen({ port, host }, async (err: any, address: any) => {
|
|
||||||
if (err) {
|
const options = {
|
||||||
console.error(err);
|
schema,
|
||||||
process.exit(1);
|
dotenv: true
|
||||||
|
};
|
||||||
|
fastify.register(env, options);
|
||||||
|
if (!isDev) {
|
||||||
|
fastify.register(serve, {
|
||||||
|
root: path.join(__dirname, './public'),
|
||||||
|
preCompressed: true
|
||||||
|
});
|
||||||
|
fastify.setNotFoundHandler(async function (request, reply) {
|
||||||
|
if (request.raw.url && request.raw.url.startsWith('/api')) {
|
||||||
|
return reply.status(404).send({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return reply.status(200).sendFile('index.html');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
console.log(`Coolify's API is listening on ${host}:${port}`);
|
fastify.register(autoLoad, {
|
||||||
await initServer();
|
dir: join(__dirname, 'plugins')
|
||||||
|
});
|
||||||
|
fastify.register(autoLoad, {
|
||||||
|
dir: join(__dirname, 'routes')
|
||||||
|
});
|
||||||
|
|
||||||
const graceful = new Graceful({ brees: [scheduler] });
|
fastify.register(cookie)
|
||||||
graceful.listen();
|
fastify.register(cors);
|
||||||
|
fastify.addHook('onRequest', async (request, reply) => {
|
||||||
|
let allowedList = ['coolify:3000'];
|
||||||
|
const { ipv4, ipv6, fqdn } = await prisma.setting.findFirst({})
|
||||||
|
|
||||||
setInterval(async () => {
|
ipv4 && allowedList.push(`${ipv4}:3000`);
|
||||||
if (!scheduler.workers.has('deployApplication')) {
|
ipv6 && allowedList.push(ipv6);
|
||||||
scheduler.run('deployApplication');
|
fqdn && allowedList.push(getDomain(fqdn));
|
||||||
|
isDev && allowedList.push('localhost:3000') && allowedList.push('localhost:3001') && allowedList.push('host.docker.internal:3001');
|
||||||
|
const remotes = await prisma.destinationDocker.findMany({ where: { remoteEngine: true, remoteVerified: true } })
|
||||||
|
if (remotes.length > 0) {
|
||||||
|
remotes.forEach(remote => {
|
||||||
|
allowedList.push(`${remote.remoteIpAddress}:3000`);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (!scheduler.workers.has('infrastructure')) {
|
if (!allowedList.includes(request.headers.host)) {
|
||||||
scheduler.run('infrastructure');
|
// console.log('not allowed', request.headers.host)
|
||||||
}
|
}
|
||||||
}, 2000)
|
})
|
||||||
|
fastify.listen({ port, host }, async (err: any, address: any) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(`Coolify's API is listening on ${host}:${port}`);
|
||||||
|
await initServer();
|
||||||
|
|
||||||
// autoUpdater
|
const graceful = new Graceful({ brees: [scheduler] });
|
||||||
setInterval(async () => {
|
graceful.listen();
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:autoUpdater")
|
|
||||||
}, isDev ? 5000 : 60000 * 15)
|
|
||||||
|
|
||||||
// cleanupStorage
|
setInterval(async () => {
|
||||||
setInterval(async () => {
|
if (!scheduler.workers.has('deployApplication')) {
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
|
scheduler.run('deployApplication');
|
||||||
}, isDev ? 6000 : 60000 * 10)
|
}
|
||||||
|
if (!scheduler.workers.has('infrastructure')) {
|
||||||
|
scheduler.run('infrastructure');
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
// checkProxies
|
// autoUpdater
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkProxies")
|
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:autoUpdater")
|
||||||
}, 10000)
|
}, isDev ? 5000 : 60000 * 15)
|
||||||
|
|
||||||
// cleanupPrismaEngines
|
// cleanupStorage
|
||||||
// setInterval(async () => {
|
setInterval(async () => {
|
||||||
// scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupPrismaEngines")
|
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupStorage")
|
||||||
// }, 60000)
|
}, isDev ? 6000 : 60000 * 10)
|
||||||
|
|
||||||
|
// checkProxies
|
||||||
|
setInterval(async () => {
|
||||||
|
scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:checkProxies")
|
||||||
|
}, 10000)
|
||||||
|
|
||||||
|
// cleanupPrismaEngines
|
||||||
|
// setInterval(async () => {
|
||||||
|
// scheduler.workers.has('infrastructure') && scheduler.workers.get('infrastructure').postMessage("action:cleanupPrismaEngines")
|
||||||
|
// }, 60000)
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
getArch(),
|
||||||
|
getIPAddress(),
|
||||||
|
configureRemoteDockers(),
|
||||||
|
])
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
await getArch();
|
|
||||||
await getIPAddress();
|
|
||||||
});
|
|
||||||
async function getIPAddress() {
|
async function getIPAddress() {
|
||||||
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
const { publicIpv4, publicIpv6 } = await import('public-ip')
|
||||||
try {
|
try {
|
||||||
@@ -175,4 +199,15 @@ async function getArch() {
|
|||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function configureRemoteDockers() {
|
||||||
|
try {
|
||||||
|
const remoteDocker = await prisma.destinationDocker.findMany({
|
||||||
|
where: { remoteVerified: true, remoteEngine: true }
|
||||||
|
});
|
||||||
|
if (remoteDocker.length > 0) {
|
||||||
|
for (const docker of remoteDocker) {
|
||||||
|
await createRemoteEngineConfiguration(docker.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
|||||||
@@ -177,9 +177,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
await prisma.build.update({ where: { id: buildId }, data: { commit } });
|
||||||
} catch (err) {
|
} catch (err) { }
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pullmergeRequestId) {
|
if (!pullmergeRequestId) {
|
||||||
if (configHash !== currentHash) {
|
if (configHash !== currentHash) {
|
||||||
@@ -359,21 +357,15 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
await saveBuildLog({ line: error, buildId, applicationId: application.id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await pAll.default(actions, { concurrency })
|
await pAll.default(actions, { concurrency })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
await th()
|
await th()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else process.exit(0);
|
} else process.exit(0);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { parentPort } from 'node:worker_threads';
|
import { parentPort } from 'node:worker_threads';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version } from '../lib/common';
|
import { asyncExecShell, cleanupDockerStorage, executeDockerCmd, isDev, prisma, startTraefikTCPProxy, generateDatabaseConfiguration, startTraefikProxy, listSettings, version, createRemoteEngineConfiguration } from '../lib/common';
|
||||||
|
|
||||||
async function autoUpdater() {
|
async function autoUpdater() {
|
||||||
try {
|
try {
|
||||||
@@ -21,7 +21,6 @@ async function autoUpdater() {
|
|||||||
const activeCount = 0
|
const activeCount = 0
|
||||||
if (activeCount === 0) {
|
if (activeCount === 0) {
|
||||||
if (!isDev) {
|
if (!isDev) {
|
||||||
console.log(`Updating Coolify to ${latestVersion}.`);
|
|
||||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
||||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
await asyncExecShell(`env | grep COOLIFY > .env`);
|
||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
@@ -35,9 +34,7 @@ async function autoUpdater() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async function checkProxies() {
|
async function checkProxies() {
|
||||||
try {
|
try {
|
||||||
@@ -45,18 +42,35 @@ async function checkProxies() {
|
|||||||
let portReachable;
|
let portReachable;
|
||||||
|
|
||||||
const { arch, ipv4, ipv6 } = await listSettings();
|
const { arch, ipv4, ipv6 } = await listSettings();
|
||||||
|
|
||||||
// Coolify Proxy local
|
// Coolify Proxy local
|
||||||
const engine = '/var/run/docker.sock';
|
const engine = '/var/run/docker.sock';
|
||||||
const localDocker = await prisma.destinationDocker.findFirst({
|
const localDocker = await prisma.destinationDocker.findFirst({
|
||||||
where: { engine, network: 'coolify' }
|
where: { engine, network: 'coolify', isCoolifyProxyUsed: true }
|
||||||
});
|
});
|
||||||
if (localDocker && localDocker.isCoolifyProxyUsed) {
|
if (localDocker) {
|
||||||
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
|
portReachable = await isReachable(80, { host: ipv4 || ipv6 })
|
||||||
if (!portReachable) {
|
if (!portReachable) {
|
||||||
await startTraefikProxy(localDocker.id);
|
await startTraefikProxy(localDocker.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Coolify Proxy remote
|
||||||
|
const remoteDocker = await prisma.destinationDocker.findMany({
|
||||||
|
where: { remoteEngine: true, remoteVerified: true }
|
||||||
|
});
|
||||||
|
if (remoteDocker.length > 0) {
|
||||||
|
for (const docker of remoteDocker) {
|
||||||
|
if (docker.isCoolifyProxyUsed) {
|
||||||
|
portReachable = await isReachable(80, { host: docker.remoteIpAddress })
|
||||||
|
if (!portReachable) {
|
||||||
|
await startTraefikProxy(docker.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await createRemoteEngineConfiguration(docker.id)
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
// TCP Proxies
|
// TCP Proxies
|
||||||
const databasesWithPublicPort = await prisma.database.findMany({
|
const databasesWithPublicPort = await prisma.database.findMany({
|
||||||
where: { publicPort: { not: null } },
|
where: { publicPort: { not: null } },
|
||||||
@@ -113,9 +127,7 @@ async function cleanupPrismaEngines() {
|
|||||||
if (stdout.trim() != null && stdout.trim() != '' && Number(stdout.trim()) > 1) {
|
if (stdout.trim() != null && stdout.trim() != '' && Number(stdout.trim()) > 1) {
|
||||||
await asyncExecShell(`killall -q -e /app/prisma-engines/query-engine -o 1m`)
|
await asyncExecShell(`killall -q -e /app/prisma-engines/query-engine -o 1m`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function cleanupStorage() {
|
async function cleanupStorage() {
|
||||||
@@ -166,9 +178,7 @@ async function cleanupStorage() {
|
|||||||
lowDiskSpace = true;
|
lowDiskSpace = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
|
await cleanupDockerStorage(destination.id, lowDiskSpace, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,22 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
const phpVersions = [
|
const phpVersions = [
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.2',
|
||||||
|
label: 'webdevops/php-apache:8.2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.2',
|
||||||
|
label: 'webdevops/php-nginx:8.2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.1',
|
||||||
|
label: 'webdevops/php-apache:8.1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.1',
|
||||||
|
label: 'webdevops/php-nginx:8.1'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'webdevops/php-apache:8.0',
|
value: 'webdevops/php-apache:8.0',
|
||||||
label: 'webdevops/php-apache:8.0'
|
label: 'webdevops/php-apache:8.0'
|
||||||
@@ -145,6 +161,22 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
value: 'webdevops/php-nginx:5.6',
|
value: 'webdevops/php-nginx:5.6',
|
||||||
label: 'webdevops/php-nginx:5.6'
|
label: 'webdevops/php-nginx:5.6'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.2-alpine',
|
||||||
|
label: 'webdevops/php-apache:8.2-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.2-alpine',
|
||||||
|
label: 'webdevops/php-nginx:8.2-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-apache:8.1-alpine',
|
||||||
|
label: 'webdevops/php-apache:8.1-alpine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'webdevops/php-nginx:8.1-alpine',
|
||||||
|
label: 'webdevops/php-nginx:8.1-alpine'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'webdevops/php-apache:8.0-alpine',
|
value: 'webdevops/php-apache:8.0-alpine',
|
||||||
label: 'webdevops/php-apache:8.0-alpine'
|
label: 'webdevops/php-apache:8.0-alpine'
|
||||||
@@ -305,11 +337,11 @@ export function setDefaultBaseImage(buildPack: string | null, deploymentType: st
|
|||||||
payload.baseImage = 'denoland/deno:latest';
|
payload.baseImage = 'denoland/deno:latest';
|
||||||
}
|
}
|
||||||
if (buildPack === 'php') {
|
if (buildPack === 'php') {
|
||||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
payload.baseImage = 'webdevops/php-apache:8.2-alpine';
|
||||||
payload.baseImages = phpVersions;
|
payload.baseImages = phpVersions;
|
||||||
}
|
}
|
||||||
if (buildPack === 'laravel') {
|
if (buildPack === 'laravel') {
|
||||||
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
|
payload.baseImage = 'webdevops/php-apache:8.2-alpine';
|
||||||
payload.baseBuildImage = 'node:18';
|
payload.baseBuildImage = 'node:18';
|
||||||
payload.baseBuildImages = nodeVersions;
|
payload.baseBuildImages = nodeVersions;
|
||||||
}
|
}
|
||||||
@@ -512,7 +544,6 @@ export async function copyBaseConfigurationFiles(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { scheduler } from './scheduler';
|
|||||||
import { supportedServiceTypesAndVersions } from './services/supportedVersions';
|
import { supportedServiceTypesAndVersions } from './services/supportedVersions';
|
||||||
import { includeServices } from './services/common';
|
import { includeServices } from './services/common';
|
||||||
|
|
||||||
export const version = '3.9.0-rc.1';
|
export const version = '3.9.2';
|
||||||
export const isDev = process.env.NODE_ENV === 'development';
|
export const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
const algorithm = 'aes-256-ctr';
|
const algorithm = 'aes-256-ctr';
|
||||||
@@ -439,7 +439,6 @@ export async function getFreeSSHLocalPort(id: string): Promise<number | boolean>
|
|||||||
return Number(alreadyConfigured.sshLocalPort)
|
return Number(alreadyConfigured.sshLocalPort)
|
||||||
}
|
}
|
||||||
const range = generateRangeArray(minPort, maxPort)
|
const range = generateRangeArray(minPort, maxPort)
|
||||||
console.log({ ports })
|
|
||||||
const availablePorts = range.filter(port => !ports.map(p => p.sshLocalPort).includes(port))
|
const availablePorts = range.filter(port => !ports.map(p => p.sshLocalPort).includes(port))
|
||||||
for (const port of availablePorts) {
|
for (const port of availablePorts) {
|
||||||
const found = await isReachable(port, { host: 'localhost' })
|
const found = await isReachable(port, { host: 'localhost' })
|
||||||
@@ -458,20 +457,21 @@ export async function createRemoteEngineConfiguration(id: string) {
|
|||||||
const { sshKey: { privateKey }, remoteIpAddress, remotePort, remoteUser } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } })
|
const { sshKey: { privateKey }, remoteIpAddress, remotePort, remoteUser } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } })
|
||||||
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 })
|
await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 })
|
||||||
// Needed for remote docker compose
|
// Needed for remote docker compose
|
||||||
const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell(`ps ax | grep [s]sh-agent | grep ssh-agent.pid | grep -v grep | wc -l`)
|
const { stdout: numberOfSSHAgentsRunning } = await asyncExecShell(`ps ax | grep [s]sh-agent | grep coolify-ssh-agent.pid | grep -v grep | wc -l`)
|
||||||
if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) {
|
if (numberOfSSHAgentsRunning !== '' && Number(numberOfSSHAgentsRunning.trim()) == 0) {
|
||||||
await asyncExecShell(`eval $(ssh-agent -sa /tmp/ssh-agent.pid)`)
|
try {
|
||||||
|
await fs.stat(`/tmp/coolify-ssh-agent.pid`)
|
||||||
|
await fs.rm(`/tmp/coolify-ssh-agent.pid`)
|
||||||
|
} catch (error) { }
|
||||||
|
await asyncExecShell(`eval $(ssh-agent -sa /tmp/coolify-ssh-agent.pid)`)
|
||||||
}
|
}
|
||||||
await asyncExecShell(`SSH_AUTH_SOCK=/tmp/ssh-agent.pid ssh-add -q ${sshKeyFile}`)
|
await asyncExecShell(`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh-add -q ${sshKeyFile}`)
|
||||||
|
|
||||||
const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell(`ps ax | grep 'ssh -F /dev/null -o StrictHostKeyChecking no -fNL ${localPort}:localhost:${remotePort}' | grep -v grep | wc -l`)
|
const { stdout: numberOfSSHTunnelsRunning } = await asyncExecShell(`ps ax | grep 'ssh -F /dev/null -o StrictHostKeyChecking no -fNL ${localPort}:localhost:${remotePort}' | grep -v grep | wc -l`)
|
||||||
if (numberOfSSHTunnelsRunning !== '' && Number(numberOfSSHTunnelsRunning.trim()) == 0) {
|
if (numberOfSSHTunnelsRunning !== '' && Number(numberOfSSHTunnelsRunning.trim()) == 0) {
|
||||||
try {
|
try {
|
||||||
await asyncExecShell(`SSH_AUTH_SOCK=/tmp/ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}`)
|
await asyncExecShell(`SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid ssh -F /dev/null -o "StrictHostKeyChecking no" -fNL ${localPort}:localhost:${remotePort} ${remoteUser}@${remoteIpAddress}`)
|
||||||
|
} catch (error) { }
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
const config = sshConfig.parse('')
|
const config = sshConfig.parse('')
|
||||||
@@ -974,9 +974,14 @@ export const createDirectories = async ({
|
|||||||
}): Promise<{ workdir: string; repodir: string }> => {
|
}): Promise<{ workdir: string; repodir: string }> => {
|
||||||
const repodir = `/tmp/build-sources/${repository}/`;
|
const repodir = `/tmp/build-sources/${repository}/`;
|
||||||
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
|
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
|
||||||
|
let workdirFound = false;
|
||||||
|
try {
|
||||||
|
workdirFound = !!(await fs.stat(workdir));
|
||||||
|
} catch (error) { }
|
||||||
|
if (workdirFound) {
|
||||||
|
await asyncExecShell(`rm -fr ${workdir}`);
|
||||||
|
}
|
||||||
await asyncExecShell(`mkdir -p ${workdir}`);
|
await asyncExecShell(`mkdir -p ${workdir}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
workdir,
|
workdir,
|
||||||
repodir
|
repodir
|
||||||
@@ -1248,7 +1253,6 @@ export async function startTraefikTCPProxy(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1436,15 +1440,13 @@ export function convertTolOldVolumeNames(type) {
|
|||||||
export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
||||||
// Cleanup old coolify images
|
// Cleanup old coolify images
|
||||||
try {
|
try {
|
||||||
let { stdout: images } = await executeDockerCmd({ dockerId, command: `docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs` })
|
let { stdout: images } = await executeDockerCmd({ dockerId, command: `docker images coollabsio/coolify --filter before="coollabsio/coolify:${version}" -q | xargs -r` })
|
||||||
|
|
||||||
images = images.trim();
|
images = images.trim();
|
||||||
if (images) {
|
if (images) {
|
||||||
await executeDockerCmd({ dockerId, command: `docker rmi -f ${images}" -q | xargs` })
|
await executeDockerCmd({ dockerId, command: `docker rmi -f ${images}" -q | xargs -r` })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
//console.log(error);
|
|
||||||
}
|
|
||||||
if (lowDiskSpace || force) {
|
if (lowDiskSpace || force) {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
if (!force) console.log(`[DEV MODE] Low disk space: ${lowDiskSpace}`);
|
if (!force) console.log(`[DEV MODE] Low disk space: ${lowDiskSpace}`);
|
||||||
@@ -1452,25 +1454,17 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId, command: `docker container prune -f --filter "label=coolify.managed=true"` })
|
await executeDockerCmd({ dockerId, command: `docker container prune -f --filter "label=coolify.managed=true"` })
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
//console.log(error);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId, command: `docker image prune -f` })
|
await executeDockerCmd({ dockerId, command: `docker image prune -f` })
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
//console.log(error);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId, command: `docker image prune -a -f` })
|
await executeDockerCmd({ dockerId, command: `docker image prune -a -f` })
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
//console.log(error);
|
|
||||||
}
|
|
||||||
// Cleanup build caches
|
// Cleanup build caches
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId, command: `docker builder prune -a -f` })
|
await executeDockerCmd({ dockerId, command: `docker builder prune -a -f` })
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
//console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ export async function removeContainer({
|
|||||||
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
await executeDockerCmd({ dockerId, command: `docker rm ${id}` })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
|||||||
|
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
||||||
|
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.plausibleAnalytics)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
|
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
@@ -333,6 +333,8 @@ async function startMinioService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
image: `${image}:${version}`,
|
image: `${image}:${version}`,
|
||||||
volumes: [`${id}-minio-data:/data`],
|
volumes: [`${id}-minio-data:/data`],
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
|
MINIO_SERVER_URL: fqdn,
|
||||||
|
MINIO_DOMAIN: getDomain(fqdn),
|
||||||
MINIO_ROOT_USER: rootUser,
|
MINIO_ROOT_USER: rootUser,
|
||||||
MINIO_ROOT_PASSWORD: rootUserPassword,
|
MINIO_ROOT_PASSWORD: rootUserPassword,
|
||||||
MINIO_BROWSER_REDIRECT_URL: fqdn
|
MINIO_BROWSER_REDIRECT_URL: fqdn
|
||||||
@@ -852,7 +854,7 @@ async function startGhostService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.ghost)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@@ -1086,7 +1088,7 @@ async function startUmamiService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
FROM ${config.postgresql.image}
|
FROM ${config.postgresql.image}
|
||||||
COPY ./schema.postgresql.sql /docker-entrypoint-initdb.d/schema.postgresql.sql`;
|
COPY ./schema.postgresql.sql /docker-entrypoint-initdb.d/schema.postgresql.sql`;
|
||||||
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile);
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.umami)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@@ -1114,6 +1116,7 @@ async function startUmamiService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
},
|
},
|
||||||
volumes: volumeMounts
|
volumes: volumeMounts
|
||||||
};
|
};
|
||||||
|
console.log(composeFile)
|
||||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
await startServiceContainers(destinationDocker.id, composeFileDestination)
|
await startServiceContainers(destinationDocker.id, composeFileDestination)
|
||||||
@@ -1167,7 +1170,7 @@ async function startHasuraService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.hasura)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@@ -1272,7 +1275,7 @@ async function startFiderService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
config.fider.environmentVariables[secret.name] = secret.value;
|
config.fider.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.fider)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@@ -1880,7 +1883,7 @@ async function startMoodleService(request: FastifyRequest<ServiceStartStop>) {
|
|||||||
config.moodle.environmentVariables[secret.name] = secret.value;
|
config.moodle.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.moodle)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
@@ -2006,7 +2009,7 @@ async function startGlitchTipService(request: FastifyRequest<ServiceStartStop>)
|
|||||||
config.glitchTip.environmentVariables[secret.name] = secret.value;
|
config.glitchTip.environmentVariables[secret.name] = secret.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { volumeMounts } = persistentVolumes(id, persistentStorage, config.glitchTip)
|
const { volumeMounts } = persistentVolumes(id, persistentStorage, config)
|
||||||
const composeFile: ComposeFile = {
|
const composeFile: ComposeFile = {
|
||||||
version: '3.8',
|
version: '3.8',
|
||||||
services: {
|
services: {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export default fp<FastifyJWTOptions>(async (fastify, opts) => {
|
|||||||
try {
|
try {
|
||||||
await request.jwtVerify()
|
await request.jwtVerify()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
|
||||||
reply.send(err)
|
reply.send(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ export async function getApplicationFromDB(id: string, teamId: string) {
|
|||||||
settings: true,
|
settings: true,
|
||||||
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
||||||
secrets: true,
|
secrets: true,
|
||||||
persistentStorage: true
|
persistentStorage: true,
|
||||||
|
connectedDatabase: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!application) {
|
if (!application) {
|
||||||
@@ -190,7 +191,8 @@ export async function getApplicationFromDBWebhook(projectId: number, branch: str
|
|||||||
settings: true,
|
settings: true,
|
||||||
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
gitSource: { include: { githubApp: true, gitlabApp: true } },
|
||||||
secrets: true,
|
secrets: true,
|
||||||
persistentStorage: true
|
persistentStorage: true,
|
||||||
|
connectedDatabase: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (applications.length === 0) {
|
if (applications.length === 0) {
|
||||||
@@ -242,7 +244,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
|||||||
denoOptions,
|
denoOptions,
|
||||||
baseImage,
|
baseImage,
|
||||||
baseBuildImage,
|
baseBuildImage,
|
||||||
deploymentType
|
deploymentType,
|
||||||
|
baseDatabaseBranch
|
||||||
} = request.body
|
} = request.body
|
||||||
if (port) port = Number(port);
|
if (port) port = Number(port);
|
||||||
if (exposePort) {
|
if (exposePort) {
|
||||||
@@ -263,22 +266,43 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
|||||||
dockerFileLocation,
|
dockerFileLocation,
|
||||||
denoMainFile
|
denoMainFile
|
||||||
});
|
});
|
||||||
await prisma.application.update({
|
if (baseDatabaseBranch) {
|
||||||
where: { id },
|
await prisma.application.update({
|
||||||
data: {
|
where: { id },
|
||||||
name,
|
data: {
|
||||||
fqdn,
|
name,
|
||||||
exposePort,
|
fqdn,
|
||||||
pythonWSGI,
|
exposePort,
|
||||||
pythonModule,
|
pythonWSGI,
|
||||||
pythonVariable,
|
pythonModule,
|
||||||
denoOptions,
|
pythonVariable,
|
||||||
baseImage,
|
denoOptions,
|
||||||
baseBuildImage,
|
baseImage,
|
||||||
deploymentType,
|
baseBuildImage,
|
||||||
...defaultConfiguration
|
deploymentType,
|
||||||
}
|
...defaultConfiguration,
|
||||||
});
|
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await prisma.application.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
fqdn,
|
||||||
|
exposePort,
|
||||||
|
pythonWSGI,
|
||||||
|
pythonModule,
|
||||||
|
pythonVariable,
|
||||||
|
denoOptions,
|
||||||
|
baseImage,
|
||||||
|
baseBuildImage,
|
||||||
|
deploymentType,
|
||||||
|
...defaultConfiguration
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return reply.code(201).send();
|
return reply.code(201).send();
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
@@ -289,7 +313,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
|
|||||||
export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) {
|
export async function saveApplicationSettings(request: FastifyRequest<SaveApplicationSettings>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot } = request.body
|
const { debug, previews, dualCerts, autodeploy, branch, projectId, isBot, isDBBranching } = request.body
|
||||||
// const isDouble = await checkDoubleBranch(branch, projectId);
|
// const isDouble = await checkDoubleBranch(branch, projectId);
|
||||||
// if (isDouble && autodeploy) {
|
// if (isDouble && autodeploy) {
|
||||||
// await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
// await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false } })
|
||||||
@@ -297,7 +321,7 @@ export async function saveApplicationSettings(request: FastifyRequest<SaveApplic
|
|||||||
// }
|
// }
|
||||||
await prisma.application.update({
|
await prisma.application.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot } } },
|
data: { fqdn: isBot ? null : undefined, settings: { update: { debug, previews, dualCerts, autodeploy, isBot, isDBBranching } } },
|
||||||
include: { destinationDocker: true }
|
include: { destinationDocker: true }
|
||||||
});
|
});
|
||||||
return reply.code(201).send();
|
return reply.code(201).send();
|
||||||
@@ -478,6 +502,7 @@ export async function deleteApplication(request: FastifyRequest<DeleteApplicatio
|
|||||||
await prisma.build.deleteMany({ where: { applicationId: id } });
|
await prisma.build.deleteMany({ where: { applicationId: id } });
|
||||||
await prisma.secret.deleteMany({ where: { applicationId: id } });
|
await prisma.secret.deleteMany({ where: { applicationId: id } });
|
||||||
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
|
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
|
||||||
|
await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: id } });
|
||||||
if (teamId === '0') {
|
if (teamId === '0') {
|
||||||
await prisma.application.deleteMany({ where: { id } });
|
await prisma.application.deleteMany({ where: { id } });
|
||||||
} else {
|
} else {
|
||||||
@@ -501,10 +526,12 @@ export async function checkDomain(request: FastifyRequest<CheckDomain>) {
|
|||||||
export async function checkDNS(request: FastifyRequest<CheckDNS>) {
|
export async function checkDNS(request: FastifyRequest<CheckDNS>) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
|
|
||||||
let { exposePort, fqdn, forceSave, dualCerts } = request.body
|
let { exposePort, fqdn, forceSave, dualCerts } = request.body
|
||||||
|
if (!fqdn) {
|
||||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
return {}
|
||||||
|
} else {
|
||||||
|
fqdn = fqdn.toLowerCase();
|
||||||
|
}
|
||||||
if (exposePort) exposePort = Number(exposePort);
|
if (exposePort) exposePort = Number(exposePort);
|
||||||
|
|
||||||
const { destinationDocker: { id: dockerId, remoteIpAddress, remoteEngine }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
|
const { destinationDocker: { id: dockerId, remoteIpAddress, remoteEngine }, exposePort: configuredPort } = await prisma.application.findUnique({ where: { id }, include: { destinationDocker: true } })
|
||||||
@@ -734,6 +761,16 @@ export async function saveBuildPack(request, reply) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function saveConnectedDatabase(request, reply) {
|
||||||
|
try {
|
||||||
|
const { id } = request.params
|
||||||
|
const { databaseId, type } = request.body
|
||||||
|
await prisma.application.update({ where: { id }, data: { connectedDatabase: { upsert: { create: { database: { connect: { id: databaseId } }, hostedDatabaseType: type }, update: { database: { connect: { id: databaseId } }, hostedDatabaseType: type } } } } })
|
||||||
|
return reply.code(201).send()
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getSecrets(request: FastifyRequest<OnlyId>) {
|
export async function getSecrets(request: FastifyRequest<OnlyId>) {
|
||||||
try {
|
try {
|
||||||
@@ -890,7 +927,6 @@ export async function getPreviews(request: FastifyRequest<OnlyId>) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -982,11 +1018,13 @@ export async function getBuildIdLogs(request: FastifyRequest<GetBuildIdLogs>) {
|
|||||||
where: { buildId, time: { gt: sequence } },
|
where: { buildId, time: { gt: sequence } },
|
||||||
orderBy: { time: 'asc' }
|
orderBy: { time: 'asc' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await prisma.build.findFirst({ where: { id: buildId } });
|
const data = await prisma.build.findFirst({ where: { id: buildId } });
|
||||||
const createdAt = day(data.createdAt).utc();
|
const createdAt = day(data.createdAt).utc();
|
||||||
return {
|
return {
|
||||||
logs,
|
logs: logs.map(log => {
|
||||||
|
log.time = Number(log.time)
|
||||||
|
return log
|
||||||
|
}),
|
||||||
took: day().diff(createdAt) / 1000,
|
took: day().diff(createdAt) / 1000,
|
||||||
status: data?.status || 'queued'
|
status: data?.status || 'queued'
|
||||||
}
|
}
|
||||||
@@ -1063,3 +1101,58 @@ export async function cancelDeployment(request: FastifyRequest<CancelDeployment>
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function createdBranchDatabase(database: any, baseDatabaseBranch: string, pullmergeRequestId: string) {
|
||||||
|
try {
|
||||||
|
if (!baseDatabaseBranch) return
|
||||||
|
const { id, type, destinationDockerId, rootUser, rootUserPassword, dbUser } = database;
|
||||||
|
if (destinationDockerId) {
|
||||||
|
if (type === 'postgresql') {
|
||||||
|
const decryptedRootUserPassword = decrypt(rootUserPassword);
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} pg_dump -d "postgresql://postgres:${decryptedRootUserPassword}@${id}:5432/${baseDatabaseBranch}" --encoding=UTF8 --schema-only -f /tmp/${baseDatabaseBranch}.dump`
|
||||||
|
})
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "CREATE DATABASE branch_${pullmergeRequestId}"`
|
||||||
|
})
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} psql -d "postgresql://postgres:${decryptedRootUserPassword}@${id}:5432/branch_${pullmergeRequestId}" -f /tmp/${baseDatabaseBranch}.dump`
|
||||||
|
})
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "ALTER DATABASE branch_${pullmergeRequestId} OWNER TO ${dbUser}"`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function removeBranchDatabase(database: any, pullmergeRequestId: string) {
|
||||||
|
try {
|
||||||
|
const { id, type, destinationDockerId, rootUser, rootUserPassword } = database;
|
||||||
|
if (destinationDockerId) {
|
||||||
|
if (type === 'postgresql') {
|
||||||
|
const decryptedRootUserPassword = decrypt(rootUserPassword);
|
||||||
|
// Terminate all connections to the database
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'branch_${pullmergeRequestId}' AND pid <> pg_backend_pid();"`
|
||||||
|
})
|
||||||
|
|
||||||
|
await executeDockerCmd({
|
||||||
|
dockerId: destinationDockerId,
|
||||||
|
command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "DROP DATABASE branch_${pullmergeRequestId}"`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { OnlyId } from '../../../../types';
|
import { OnlyId } from '../../../../types';
|
||||||
import { cancelDeployment, checkDNS, checkDomain, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildLogs, getBuildPack, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getSecrets, getStorages, getUsage, listApplications, newApplication, restartApplication, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication } from './handlers';
|
import { cancelDeployment, checkDNS, checkDomain, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildLogs, getBuildPack, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getSecrets, getStorages, getUsage, listApplications, newApplication, restartApplication, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication } from './handlers';
|
||||||
|
|
||||||
import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuildLogs, GetImages, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types';
|
import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuildLogs, GetImages, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types';
|
||||||
|
|
||||||
@@ -55,6 +55,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request));
|
fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request));
|
||||||
fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply));
|
fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply));
|
||||||
|
|
||||||
|
fastify.post('/:id/configuration/database', async (request, reply) => await saveConnectedDatabase(request, reply));
|
||||||
|
|
||||||
fastify.get<OnlyId>('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
fastify.get<OnlyId>('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request));
|
||||||
fastify.post<OnlyId>('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
fastify.post<OnlyId>('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply));
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ export interface SaveApplication extends OnlyId {
|
|||||||
denoOptions: string,
|
denoOptions: string,
|
||||||
baseImage: string,
|
baseImage: string,
|
||||||
baseBuildImage: string,
|
baseBuildImage: string,
|
||||||
deploymentType: string
|
deploymentType: string,
|
||||||
|
baseDatabaseBranch: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export interface SaveApplicationSettings extends OnlyId {
|
export interface SaveApplicationSettings extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string; };
|
||||||
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; };
|
Body: { debug: boolean; previews: boolean; dualCerts: boolean; autodeploy: boolean; branch: string; projectId: number; isBot: boolean; isDBBranching: boolean };
|
||||||
}
|
}
|
||||||
export interface DeleteApplication extends OnlyId {
|
export interface DeleteApplication extends OnlyId {
|
||||||
Querystring: { domain: string; };
|
Querystring: { domain: string; };
|
||||||
|
|||||||
@@ -280,15 +280,12 @@ export async function startDatabase(request: FastifyRequest<OnlyId>) {
|
|||||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker volume create ${volumeName}` })
|
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker volume create ${volumeName}` })
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} up -d` })
|
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose -f ${composeFileDestination} up -d` })
|
||||||
if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
|
if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
|
||||||
return {};
|
return {};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
|
||||||
throw {
|
throw {
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export async function listDestinations(request: FastifyRequest<ListDestinations>
|
|||||||
destinations
|
destinations
|
||||||
}
|
}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,6 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +160,6 @@ export async function startProxy(request: FastifyRequest<Proxy>) {
|
|||||||
await startTraefikProxy(id);
|
await startTraefikProxy(id);
|
||||||
return {}
|
return {}
|
||||||
} catch ({ status, message }) {
|
} catch ({ status, message }) {
|
||||||
console.log({ status, message })
|
|
||||||
await stopTraefikProxy(id);
|
await stopTraefikProxy(id);
|
||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
@@ -205,23 +202,21 @@ export async function assignSSHKey(request: FastifyRequest) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function verifyRemoteDockerEngine(request: FastifyRequest, reply: FastifyReply) {
|
export async function verifyRemoteDockerEngine(request: FastifyRequest<OnlyId>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
await createRemoteEngineConfiguration(id);
|
await createRemoteEngineConfiguration(id);
|
||||||
|
const { remoteIpAddress, remoteUser, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } })
|
||||||
const { remoteIpAddress, remoteUser, network } = await prisma.destinationDocker.findFirst({ where: { id } })
|
|
||||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||||
|
|
||||||
if (!stdout) {
|
if (!stdout) {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||||
}
|
}
|
||||||
const { stdout:coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
const { stdout: coolifyNetwork } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"`);
|
||||||
|
|
||||||
if (!coolifyNetwork) {
|
if (!coolifyNetwork) {
|
||||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable coolify-infra`);
|
||||||
}
|
}
|
||||||
|
if (isCoolifyProxyUsed) await startTraefikProxy(id);
|
||||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||||
return reply.code(201).send()
|
return reply.code(201).send()
|
||||||
|
|
||||||
@@ -234,7 +229,7 @@ export async function getDestinationStatus(request: FastifyRequest<OnlyId>) {
|
|||||||
try {
|
try {
|
||||||
const { id } = request.params
|
const { id } = request.params
|
||||||
const destination = await prisma.destinationDocker.findUnique({ where: { id } })
|
const destination = await prisma.destinationDocker.findUnique({ where: { id } })
|
||||||
const isRunning = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy' })
|
const isRunning = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy', remove: true })
|
||||||
return {
|
return {
|
||||||
isRunning
|
isRunning
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
|
|
||||||
fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request));
|
fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request));
|
||||||
|
|
||||||
fastify.post('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply));
|
fastify.post<OnlyId>('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default root;
|
export default root;
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export async function update(request: FastifyRequest<Update>) {
|
|||||||
);
|
);
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
console.log(latestVersion);
|
|
||||||
await asyncSleep(2000);
|
await asyncSleep(2000);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -78,10 +77,9 @@ export async function restartCoolify(request: FastifyRequest<any>) {
|
|||||||
const teamId = request.user.teamId;
|
const teamId = request.user.teamId;
|
||||||
if (teamId === '0') {
|
if (teamId === '0') {
|
||||||
if (!isDev) {
|
if (!isDev) {
|
||||||
await asyncExecShell(`docker restart coolify`);
|
asyncExecShell(`docker restart coolify`);
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
console.log('Restarting Coolify')
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,10 +560,7 @@ export async function activateWordpressFtp(request: FastifyRequest<ActivateWordp
|
|||||||
command: `docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
command: `docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error);
|
|
||||||
//
|
|
||||||
}
|
|
||||||
const volumes = [
|
const volumes = [
|
||||||
`${id}-wordpress-data:/home/${ftpUser}/wordpress`,
|
`${id}-wordpress-data:/home/${ftpUser}/wordpress`,
|
||||||
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
`${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys'
|
||||||
@@ -642,9 +639,7 @@ export async function activateWordpressFtp(request: FastifyRequest<ActivateWordp
|
|||||||
await asyncExecShell(
|
await asyncExecShell(
|
||||||
`rm -fr ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
`rm -fr ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) { }
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
fqdn,
|
fqdn,
|
||||||
|
isAPIDebuggingEnabled,
|
||||||
isRegistrationEnabled,
|
isRegistrationEnabled,
|
||||||
dualCerts,
|
dualCerts,
|
||||||
minPort,
|
minPort,
|
||||||
@@ -39,7 +40,7 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
const { id } = await listSettings();
|
const { id } = await listSettings();
|
||||||
await prisma.setting.update({
|
await prisma.setting.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers }
|
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled }
|
||||||
});
|
});
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { OnlyId } from "../../../../types"
|
|||||||
export interface SaveSettings {
|
export interface SaveSettings {
|
||||||
Body: {
|
Body: {
|
||||||
fqdn: string,
|
fqdn: string,
|
||||||
|
isAPIDebuggingEnabled: boolean,
|
||||||
isRegistrationEnabled: boolean,
|
isRegistrationEnabled: boolean,
|
||||||
dualCerts: boolean,
|
dualCerts: boolean,
|
||||||
minPort: number,
|
minPort: number,
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import cuid from "cuid";
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { encrypt, errorHandler, getUIUrl, isDev, prisma } from "../../../lib/common";
|
import { encrypt, errorHandler, getUIUrl, isDev, prisma } from "../../../lib/common";
|
||||||
import { checkContainer, removeContainer } from "../../../lib/docker";
|
import { checkContainer, removeContainer } from "../../../lib/docker";
|
||||||
import { scheduler } from "../../../lib/scheduler";
|
import { createdBranchDatabase, getApplicationFromDBWebhook, removeBranchDatabase } from "../../api/v1/applications/handlers";
|
||||||
import { getApplicationFromDBWebhook } from "../../api/v1/applications/handlers";
|
|
||||||
|
|
||||||
import type { FastifyReply, FastifyRequest } from "fastify";
|
import type { FastifyReply, FastifyRequest } from "fastify";
|
||||||
import type { GitHubEvents, InstallGithub } from "./types";
|
import type { GitHubEvents, InstallGithub } from "./types";
|
||||||
@@ -67,7 +66,6 @@ export async function configureGitHubApp(request, reply) {
|
|||||||
}
|
}
|
||||||
export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promise<any> {
|
export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const allowedGithubEvents = ['push', 'pull_request'];
|
const allowedGithubEvents = ['push', 'pull_request'];
|
||||||
const allowedActions = ['opened', 'reopened', 'synchronize', 'closed'];
|
const allowedActions = ['opened', 'reopened', 'synchronize', 'closed'];
|
||||||
const githubEvent = request.headers['x-github-event']?.toString().toLowerCase();
|
const githubEvent = request.headers['x-github-event']?.toString().toLowerCase();
|
||||||
@@ -102,8 +100,7 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
|
|||||||
const checksum = Buffer.from(githubSignature, 'utf8');
|
const checksum = Buffer.from(githubSignature, 'utf8');
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
|
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
|
||||||
console.log('SHA256 checksum failed. Are you doing something fishy?')
|
throw { status: 500, message: 'SHA256 checksum failed. Are you doing something fishy?' }
|
||||||
// throw { status: 500, message: 'SHA256 checksum failed. Are you doing something fishy?'
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +130,6 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
|
|||||||
where: { id: application.id },
|
where: { id: application.id },
|
||||||
data: { updatedAt: new Date() }
|
data: { updatedAt: new Date() }
|
||||||
});
|
});
|
||||||
console.log(application.id)
|
|
||||||
|
|
||||||
await prisma.build.create({
|
await prisma.build.create({
|
||||||
data: {
|
data: {
|
||||||
id: buildId,
|
id: buildId,
|
||||||
@@ -178,6 +173,16 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
|
|||||||
where: { id: application.id },
|
where: { id: application.id },
|
||||||
data: { updatedAt: new Date() }
|
data: { updatedAt: new Date() }
|
||||||
});
|
});
|
||||||
|
if (application.connectedDatabase && pullmergeRequestAction === 'opened' || pullmergeRequestAction === 'reopened') {
|
||||||
|
// Coolify hosted database
|
||||||
|
if (application.connectedDatabase.databaseId) {
|
||||||
|
const databaseId = application.connectedDatabase.databaseId;
|
||||||
|
const database = await prisma.database.findUnique({ where: { id: databaseId } });
|
||||||
|
if (database) {
|
||||||
|
await createdBranchDatabase(database, application.connectedDatabase.hostedDatabaseDBName, pullmergeRequestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
await prisma.build.create({
|
await prisma.build.create({
|
||||||
data: {
|
data: {
|
||||||
id: buildId,
|
id: buildId,
|
||||||
@@ -197,9 +202,17 @@ export async function gitHubEvents(request: FastifyRequest<GitHubEvents>): Promi
|
|||||||
} else if (pullmergeRequestAction === 'closed') {
|
} else if (pullmergeRequestAction === 'closed') {
|
||||||
if (application.destinationDockerId) {
|
if (application.destinationDockerId) {
|
||||||
const id = `${application.id}-${pullmergeRequestId}`;
|
const id = `${application.id}-${pullmergeRequestId}`;
|
||||||
await removeContainer({ id, dockerId: application.destinationDocker.id });
|
try {
|
||||||
|
await removeContainer({ id, dockerId: application.destinationDocker.id });
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
if (application.connectedDatabase.databaseId) {
|
||||||
|
const databaseId = application.connectedDatabase.databaseId;
|
||||||
|
const database = await prisma.database.findUnique({ where: { id: databaseId } });
|
||||||
|
if (database) {
|
||||||
|
await removeBranchDatabase(database, pullmergeRequestId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export async function gitLabEvents(request: FastifyRequest<GitLabEvents>) {
|
|||||||
await prisma.build.create({
|
await prisma.build.create({
|
||||||
data: {
|
data: {
|
||||||
id: buildId,
|
id: buildId,
|
||||||
pullmergeRequestId,
|
pullmergeRequestId: pullmergeRequestId.toString(),
|
||||||
sourceBranch,
|
sourceBranch,
|
||||||
applicationId: application.id,
|
applicationId: application.id,
|
||||||
destinationDockerId: application.destinationDocker.id,
|
destinationDockerId: application.destinationDocker.id,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
|
import { OnlyId } from '../../../types';
|
||||||
import { remoteTraefikConfiguration, traefikConfiguration, traefikOtherConfiguration } from './handlers';
|
import { remoteTraefikConfiguration, traefikConfiguration, traefikOtherConfiguration } from './handlers';
|
||||||
import { TraefikOtherConfiguration } from './types';
|
import { TraefikOtherConfiguration } from './types';
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
fastify.get('/main.json', async (request, reply) => traefikConfiguration(request, reply));
|
fastify.get('/main.json', async (request, reply) => traefikConfiguration(request, reply));
|
||||||
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request));
|
fastify.get<TraefikOtherConfiguration>('/other.json', async (request, reply) => traefikOtherConfiguration(request));
|
||||||
|
|
||||||
fastify.get('/remote/:id', async (request) => remoteTraefikConfiguration(request));
|
fastify.get<OnlyId>('/remote/:id', async (request) => remoteTraefikConfiguration(request));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default root;
|
export default root;
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
"@popperjs/core": "2.11.6",
|
"@popperjs/core": "2.11.6",
|
||||||
"@sveltejs/kit": "1.0.0-next.405",
|
"@sveltejs/kit": "1.0.0-next.405",
|
||||||
"@types/js-cookie": "3.0.2",
|
"@types/js-cookie": "3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.35.1",
|
"@typescript-eslint/eslint-plugin": "5.36.1",
|
||||||
"@typescript-eslint/parser": "5.35.1",
|
"@typescript-eslint/parser": "5.36.1",
|
||||||
"autoprefixer": "10.4.8",
|
"autoprefixer": "10.4.8",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
"eslint": "8.22.0",
|
"eslint": "8.23.0",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-plugin-svelte3": "4.0.0",
|
"eslint-plugin-svelte3": "4.0.0",
|
||||||
"flowbite": "1.5.2",
|
"flowbite": "1.5.2",
|
||||||
@@ -31,21 +31,21 @@
|
|||||||
"postcss": "8.4.16",
|
"postcss": "8.4.16",
|
||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
"prettier-plugin-svelte": "2.7.0",
|
"prettier-plugin-svelte": "2.7.0",
|
||||||
"svelte": "3.49.0",
|
"svelte": "3.50.0",
|
||||||
"svelte-check": "2.8.1",
|
"svelte-check": "2.9.0",
|
||||||
"svelte-preprocess": "4.10.7",
|
"svelte-preprocess": "4.10.7",
|
||||||
"tailwindcss": "3.1.8",
|
"tailwindcss": "3.1.8",
|
||||||
"tailwindcss-scrollbar": "0.1.0",
|
"tailwindcss-scrollbar": "0.1.0",
|
||||||
"tslib": "2.4.0",
|
"tslib": "2.4.0",
|
||||||
"typescript": "4.7.4",
|
"typescript": "4.8.2",
|
||||||
"vite": "3.0.5"
|
"vite": "3.1.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/adapter-static": "1.0.0-next.39",
|
"@sveltejs/adapter-static": "1.0.0-next.39",
|
||||||
"@tailwindcss/typography": "^0.5.4",
|
"@tailwindcss/typography": "^0.5.7",
|
||||||
"cuid": "2.1.8",
|
"cuid": "2.1.8",
|
||||||
"daisyui": "2.24.0",
|
"daisyui": "2.24.2",
|
||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"p-limit": "4.0.0",
|
"p-limit": "4.0.0",
|
||||||
"svelte-select": "4.4.7",
|
"svelte-select": "4.4.7",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
updateStatus.loading = true;
|
updateStatus.loading = true;
|
||||||
try {
|
try {
|
||||||
if (dev) {
|
if (dev) {
|
||||||
console.log(`updating to ${latestVersion}`);
|
|
||||||
await asyncSleep(4000);
|
await asyncSleep(4000);
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,8 +20,7 @@
|
|||||||
let usageInterval: any;
|
let usageInterval: any;
|
||||||
let loading = {
|
let loading = {
|
||||||
usage: false,
|
usage: false,
|
||||||
cleanup: false,
|
cleanup: false
|
||||||
restart: false
|
|
||||||
};
|
};
|
||||||
import { addToast, appSession } from '$lib/store';
|
import { addToast, appSession } from '$lib/store';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
@@ -34,25 +33,7 @@
|
|||||||
usage = data.usage;
|
usage = data.usage;
|
||||||
loading.usage = false;
|
loading.usage = false;
|
||||||
}
|
}
|
||||||
async function restartCoolify() {
|
|
||||||
const sure = confirm(
|
|
||||||
'Are you sure you would like to restart Coolify? Currently running deployments will be stopped and restarted.'
|
|
||||||
);
|
|
||||||
if (sure) {
|
|
||||||
loading.restart = true;
|
|
||||||
try {
|
|
||||||
await post(`/internal/restart`, {});
|
|
||||||
addToast({
|
|
||||||
type: 'success',
|
|
||||||
message: 'Coolify restarted successfully. It will take a moment.'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
return errorNotification(error);
|
|
||||||
} finally {
|
|
||||||
loading.restart = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
clearInterval(usageInterval);
|
clearInterval(usageInterval);
|
||||||
});
|
});
|
||||||
@@ -89,15 +70,8 @@
|
|||||||
<h1 class="title lg:text-3xl">Hardware Details</h1>
|
<h1 class="title lg:text-3xl">Hardware Details</h1>
|
||||||
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0">
|
<div class="flex lg:flex-row flex-col space-x-0 lg:space-x-2 space-y-2 lg:space-y-0">
|
||||||
{#if $appSession.teamId === '0'}
|
{#if $appSession.teamId === '0'}
|
||||||
<button
|
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
|
||||||
on:click={manuallyCleanupStorage}
|
>Cleanup Storage</button
|
||||||
class:loading={loading.cleanup}
|
|
||||||
class="btn btn-sm">Cleanup Storage</button
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
on:click={restartCoolify}
|
|
||||||
class:loading={loading.restart}
|
|
||||||
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
|
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -306,7 +306,7 @@
|
|||||||
"change_language": "Change Language",
|
"change_language": "Change Language",
|
||||||
"permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.",
|
"permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.",
|
||||||
"domain_removed": "Domain removed",
|
"domain_removed": "Domain removed",
|
||||||
"ssl_explainer": "If you specify <span class='text-settings font-bold'>https</span>, Coolify will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-settings font-bold'>www</span>, Coolify will be redirected (302) from non-www and vice versa.<br><br><span class='text-settings font-bold'>WARNING:</span> If you change an already set domain, it will brake webhooks and other integrations! You need to manually update them.",
|
"ssl_explainer": "If you specify <span class='text-settings font-bold'>https</span>, Coolify will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-settings font-bold'>www</span>, Coolify will be redirected (302) from non-www and vice versa.<br><br><span class='text-settings font-bold'>WARNING:</span> If you change an already set domain, it will break webhooks and other integrations! You need to manually update them.",
|
||||||
"must_remove_domain_before_changing": "Must remove the domain before you can change this setting.",
|
"must_remove_domain_before_changing": "Must remove the domain before you can change this setting.",
|
||||||
"registration_allowed": "Registration allowed?",
|
"registration_allowed": "Registration allowed?",
|
||||||
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import cuid from 'cuid';
|
|||||||
import { writable, readable, type Writable } from 'svelte/store';
|
import { writable, readable, type Writable } from 'svelte/store';
|
||||||
|
|
||||||
interface AppSession {
|
interface AppSession {
|
||||||
|
registrationEnabled: boolean;
|
||||||
ipv4: string | null,
|
ipv4: string | null,
|
||||||
ipv6: string | null,
|
ipv6: string | null,
|
||||||
version: string | null,
|
version: string | null,
|
||||||
@@ -45,6 +46,26 @@ export const appSession: Writable<AppSession> = writable({
|
|||||||
supportedServiceTypesAndVersions: []
|
supportedServiceTypesAndVersions: []
|
||||||
});
|
});
|
||||||
export const disabledButton: Writable<boolean> = writable(false);
|
export const disabledButton: Writable<boolean> = writable(false);
|
||||||
|
export const isDeploymentEnabled: Writable<boolean> = writable(false);
|
||||||
|
export function checkIfDeploymentEnabledApplications(isAdmin: boolean, application: any) {
|
||||||
|
return (
|
||||||
|
isAdmin &&
|
||||||
|
(application.fqdn || application.settings.isBot) &&
|
||||||
|
application.gitSource &&
|
||||||
|
application.repository &&
|
||||||
|
application.destinationDocker &&
|
||||||
|
application.buildPack
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export function checkIfDeploymentEnabledServices(isAdmin: boolean, service: any) {
|
||||||
|
return (
|
||||||
|
isAdmin &&
|
||||||
|
service.fqdn &&
|
||||||
|
service.destinationDocker &&
|
||||||
|
service.version &&
|
||||||
|
service.type
|
||||||
|
);
|
||||||
|
}
|
||||||
export const status: Writable<any> = writable({
|
export const status: Writable<any> = writable({
|
||||||
application: {
|
application: {
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let baseSettings: any;
|
export let baseSettings: any;
|
||||||
export let supportedServiceTypesAndVersions: any;
|
export let supportedServiceTypesAndVersions: any;
|
||||||
|
$appSession.registrationEnabled = baseSettings.registrationEnabled;
|
||||||
$appSession.ipv4 = baseSettings.ipv4;
|
$appSession.ipv4 = baseSettings.ipv4;
|
||||||
$appSession.ipv6 = baseSettings.ipv6;
|
$appSession.ipv6 = baseSettings.ipv6;
|
||||||
$appSession.version = baseSettings.version;
|
$appSession.version = baseSettings.version;
|
||||||
@@ -142,7 +143,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentcolor"
|
stroke="currentcolor"
|
||||||
@@ -202,7 +203,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -232,7 +233,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -268,7 +269,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -295,7 +296,7 @@
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
class="h-9 w-9"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -319,11 +320,10 @@
|
|||||||
class="icons bg-coolgray-200"
|
class="icons bg-coolgray-200"
|
||||||
class:text-iam={$page.url.pathname.startsWith('/iam')}
|
class:text-iam={$page.url.pathname.startsWith('/iam')}
|
||||||
class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')}
|
class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')}
|
||||||
data-tip="IAM"
|
|
||||||
><svg
|
><svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
class="h-9 w-9"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -344,12 +344,11 @@
|
|||||||
class="icons bg-coolgray-200"
|
class="icons bg-coolgray-200"
|
||||||
class:text-settings={$page.url.pathname.startsWith('/settings')}
|
class:text-settings={$page.url.pathname.startsWith('/settings')}
|
||||||
class:bg-coolgray-500={$page.url.pathname.startsWith('/settings')}
|
class:bg-coolgray-500={$page.url.pathname.startsWith('/settings')}
|
||||||
data-tip="Settings"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-8 w-8"
|
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
class="h-9 w-9"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -367,12 +366,11 @@
|
|||||||
<div
|
<div
|
||||||
id="logout"
|
id="logout"
|
||||||
class="icons bg-coolgray-200 hover:text-error"
|
class="icons bg-coolgray-200 hover:text-error"
|
||||||
data-tip="Logout"
|
|
||||||
on:click={logout}
|
on:click={logout}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="ml-1 h-7 w-7"
|
class="ml-1 h-8 w-8"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
@@ -406,7 +404,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<main>
|
<main>
|
||||||
<div class="pl-14 lg:px-20">
|
<div class={$appSession.userId ? 'pl-14 lg:px-20' : null}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -65,7 +65,6 @@
|
|||||||
}
|
}
|
||||||
dispatch('refresh');
|
dispatch('refresh');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,23 +60,26 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
|
import {
|
||||||
|
appSession,
|
||||||
|
status,
|
||||||
|
location,
|
||||||
|
setLocation,
|
||||||
|
addToast,
|
||||||
|
isDeploymentEnabled,
|
||||||
|
checkIfDeploymentEnabledApplications
|
||||||
|
} from '$lib/store';
|
||||||
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
|
|
||||||
let statusInterval: any;
|
let statusInterval: any;
|
||||||
let forceDelete = false;
|
let forceDelete = false;
|
||||||
$disabledButton =
|
|
||||||
!$appSession.isAdmin ||
|
|
||||||
(!application.fqdn && !application.settings.isBot) ||
|
|
||||||
!application.gitSource ||
|
|
||||||
!application.repository ||
|
|
||||||
!application.destinationDocker ||
|
|
||||||
!application.buildPack;
|
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
|
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||||
|
|
||||||
async function handleDeploySubmit(forceRebuild = false) {
|
async function handleDeploySubmit(forceRebuild = false) {
|
||||||
|
if (!$isDeploymentEnabled) return;
|
||||||
try {
|
try {
|
||||||
const { buildId } = await post(`/applications/${id}/deploy`, {
|
const { buildId } = await post(`/applications/${id}/deploy`, {
|
||||||
...application,
|
...application,
|
||||||
@@ -106,7 +109,7 @@
|
|||||||
await del(`/applications/${id}`, { id, force });
|
await del(`/applications/${id}`, { id, force });
|
||||||
return await goto(`/applications`);
|
return await goto(`/applications`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message.startsWith(`Command failed: SSH_AUTH_SOCK=/tmp/ssh-agent.pid`)) {
|
if (error.message.startsWith(`Command failed: SSH_AUTH_SOCK=/tmp/coolify-ssh-agent.pid`)) {
|
||||||
forceDelete = true;
|
forceDelete = true;
|
||||||
}
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
@@ -161,6 +164,7 @@
|
|||||||
$status.application.isExited = false;
|
$status.application.isExited = false;
|
||||||
$status.application.loading = false;
|
$status.application.loading = false;
|
||||||
$location = null;
|
$location = null;
|
||||||
|
$isDeploymentEnabled = false;
|
||||||
clearInterval(statusInterval);
|
clearInterval(statusInterval);
|
||||||
});
|
});
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -214,7 +218,7 @@
|
|||||||
{#if $status.application.isExited}
|
{#if $status.application.isExited}
|
||||||
<a
|
<a
|
||||||
id="applicationerror"
|
id="applicationerror"
|
||||||
href={!$disabledButton ? `/applications/${id}/logs` : null}
|
href={$isDeploymentEnabled ? `/applications/${id}/logs` : null}
|
||||||
class="icons bg-transparent text-sm flex items-center text-error"
|
class="icons bg-transparent text-sm flex items-center text-error"
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
>
|
>
|
||||||
@@ -240,7 +244,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if $status.application.initialLoading}
|
{#if $status.application.initialLoading}
|
||||||
<button
|
<button
|
||||||
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out hover:bg-transparent"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -266,7 +270,7 @@
|
|||||||
id="stop"
|
id="stop"
|
||||||
on:click={stopApplication}
|
on:click={stopApplication}
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={$disabledButton}
|
disabled={!$isDeploymentEnabled}
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2 text-error"
|
class="icons bg-transparent text-sm flex items-center space-x-2 text-error"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -290,7 +294,7 @@
|
|||||||
id="restart"
|
id="restart"
|
||||||
on:click={restartApplication}
|
on:click={restartApplication}
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={$disabledButton}
|
disabled={!$isDeploymentEnabled}
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2"
|
class="icons bg-transparent text-sm flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -314,7 +318,7 @@
|
|||||||
<button
|
<button
|
||||||
id="forceredeploy"
|
id="forceredeploy"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={$disabledButton}
|
disabled={!$isDeploymentEnabled}
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2"
|
class="icons bg-transparent text-sm flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -341,7 +345,7 @@
|
|||||||
<button
|
<button
|
||||||
id="deploy"
|
id="deploy"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={$disabledButton}
|
disabled={!$isDeploymentEnabled}
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2 text-success"
|
class="icons bg-transparent text-sm flex items-center space-x-2 text-success"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -364,14 +368,17 @@
|
|||||||
|
|
||||||
<div class="border border-coolgray-500 h-8" />
|
<div class="border border-coolgray-500 h-8" />
|
||||||
<a
|
<a
|
||||||
id="configurations"
|
href={$isDeploymentEnabled ? `/applications/${id}` : null}
|
||||||
href={!$disabledButton ? `/applications/${id}` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-yellow-500 rounded"
|
class="hover:text-yellow-500 rounded"
|
||||||
class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
|
class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
|
||||||
>
|
>
|
||||||
<button disabled={$disabledButton} class="icons bg-transparent text-sm">
|
<button
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
id="configurations"
|
||||||
|
class="icons bg-transparent text-sm"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
@@ -395,15 +402,16 @@
|
|||||||
</svg></button
|
</svg></button
|
||||||
></a
|
></a
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<Tooltip triggeredBy="#configurations">Configurations</Tooltip>
|
||||||
<a
|
<a
|
||||||
id="secrets"
|
href={$isDeploymentEnabled ? `/applications/${id}/secrets` : null}
|
||||||
href={!$disabledButton ? `/applications/${id}/secrets` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-pink-500 rounded"
|
class="hover:text-pink-500 rounded"
|
||||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
|
class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
|
||||||
>
|
>
|
||||||
<button disabled={$disabledButton} class="icons bg-transparent text-sm">
|
<button id="secrets" disabled={!$isDeploymentEnabled} class="icons bg-transparent text-sm">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
@@ -423,15 +431,19 @@
|
|||||||
</svg></button
|
</svg></button
|
||||||
></a
|
></a
|
||||||
>
|
>
|
||||||
|
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
|
||||||
<a
|
<a
|
||||||
id="persistentstorages"
|
href={$isDeploymentEnabled ? `/applications/${id}/storages` : null}
|
||||||
href={!$disabledButton ? `/applications/${id}/storages` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-pink-500 rounded"
|
class="hover:text-pink-500 rounded"
|
||||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/storages`}
|
class:text-pink-500={$page.url.pathname === `/applications/${id}/storages`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storages`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storages`}
|
||||||
>
|
>
|
||||||
<button disabled={$disabledButton} class="icons bg-transparent text-sm">
|
<button
|
||||||
|
id="persistentstorages"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
@@ -449,16 +461,16 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button></a
|
</button></a
|
||||||
>
|
>
|
||||||
|
<Tooltip triggeredBy="#persistentstorages">Persistent Storages</Tooltip>
|
||||||
{#if !application.settings.isBot}
|
{#if !application.settings.isBot}
|
||||||
<a
|
<a
|
||||||
id="previews"
|
href={$isDeploymentEnabled ? `/applications/${id}/previews` : null}
|
||||||
href={!$disabledButton ? `/applications/${id}/previews` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-orange-500 rounded"
|
class="hover:text-orange-500 rounded"
|
||||||
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||||
>
|
>
|
||||||
<button disabled={$disabledButton} class="icons bg-transparent text-sm">
|
<button id="previews" disabled={!$isDeploymentEnabled} class="icons bg-transparent text-sm">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
@@ -478,18 +490,19 @@
|
|||||||
</svg></button
|
</svg></button
|
||||||
></a
|
></a
|
||||||
>
|
>
|
||||||
|
<Tooltip triggeredBy="#previews">Previews</Tooltip>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="border border-coolgray-500 h-8" />
|
<div class="border border-coolgray-500 h-8" />
|
||||||
<a
|
<a
|
||||||
id="applicationlogs"
|
href={$isDeploymentEnabled && $status.application.isRunning ? `/applications/${id}/logs` : null}
|
||||||
href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-sky-500 rounded"
|
class="hover:text-sky-500 rounded"
|
||||||
class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
|
class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
disabled={$disabledButton || !$status.application.isRunning}
|
id="applicationlogs"
|
||||||
|
disabled={!$isDeploymentEnabled || !$status.application.isRunning}
|
||||||
class="icons bg-transparent text-sm"
|
class="icons bg-transparent text-sm"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -511,15 +524,15 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button></a
|
</button></a
|
||||||
>
|
>
|
||||||
|
<Tooltip triggeredBy="#applicationlogs">Application Logs</Tooltip>
|
||||||
<a
|
<a
|
||||||
id="buildlogs"
|
href={$isDeploymentEnabled ? `/applications/${id}/logs/build` : null}
|
||||||
href={!$disabledButton ? `/applications/${id}/logs/build` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-red-500 rounded"
|
class="hover:text-red-500 rounded"
|
||||||
class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
||||||
>
|
>
|
||||||
<button disabled={$disabledButton} class="icons bg-transparent text-sm">
|
<button id="buildlogs" disabled={!$isDeploymentEnabled} class="icons bg-transparent text-sm">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
@@ -542,10 +555,12 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button></a
|
</button></a
|
||||||
>
|
>
|
||||||
|
<Tooltip triggeredBy="#buildlogs">Build Logs</Tooltip>
|
||||||
<div class="border border-coolgray-500 h-8" />
|
<div class="border border-coolgray-500 h-8" />
|
||||||
|
|
||||||
{#if forceDelete}
|
{#if forceDelete}
|
||||||
<button
|
<button
|
||||||
|
id="forcedelete"
|
||||||
on:click={() => deleteApplication(application.name, true)}
|
on:click={() => deleteApplication(application.name, true)}
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!$appSession.isAdmin}
|
disabled={!$appSession.isAdmin}
|
||||||
@@ -555,6 +570,7 @@
|
|||||||
>
|
>
|
||||||
Force Delete
|
Force Delete
|
||||||
</button>
|
</button>
|
||||||
|
<Tooltip triggeredBy="#forcedelete">Force Delete</Tooltip>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
id="delete"
|
id="delete"
|
||||||
@@ -566,14 +582,7 @@
|
|||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</button>
|
</button>
|
||||||
|
<Tooltip triggeredBy="#delete">Delete</Tooltip>
|
||||||
{/if}
|
{/if}
|
||||||
</nav>
|
</nav>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<Tooltip triggeredBy="#configurations">Configurations</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#persistentstorages">Persistent Storages</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#previews">Previews</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#applicationlogs">Application Logs</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#buildlogs">Build Logs</Tooltip>
|
|
||||||
<Tooltip triggeredBy="#delete">Delete</Tooltip>
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
async function loadBranches() {
|
async function loadBranches() {
|
||||||
try {
|
try {
|
||||||
loading.branches = true;
|
loading.branches = true;
|
||||||
|
publicRepositoryLink = publicRepositoryLink.trim();
|
||||||
const protocol = publicRepositoryLink.split(':')[0];
|
const protocol = publicRepositoryLink.split(':')[0];
|
||||||
const gitUrl = publicRepositoryLink.replace('http://', '').replace('https://', '');
|
const gitUrl = publicRepositoryLink.replace('http://', '').replace('https://', '');
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
<script context="module" lang="ts">
|
||||||
|
import type { Load } from '@sveltejs/kit';
|
||||||
|
export const load: Load = async () => {
|
||||||
|
try {
|
||||||
|
const response = await get(`/databases`);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...response
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
error: new Error(error)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export let databases: any = [];
|
||||||
|
import { get, post } from '$lib/api';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
import { appSession } from '$lib/store';
|
||||||
|
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
|
||||||
|
import { errorNotification } from '$lib/common';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
const from = $page.url.searchParams.get('from');
|
||||||
|
|
||||||
|
let remoteDatabase = {
|
||||||
|
name: null,
|
||||||
|
type: null,
|
||||||
|
host: null,
|
||||||
|
port: null,
|
||||||
|
user: null,
|
||||||
|
password: null,
|
||||||
|
database: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const ownDatabases = databases.filter((database: any) => {
|
||||||
|
if (database.teams[0].id === $appSession.teamId) {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const otherDatabases = databases.filter((database: any) => {
|
||||||
|
if (database.teams[0].id !== $appSession.teamId) {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function addCoolifyDatabase(database: any) {
|
||||||
|
try {
|
||||||
|
await post(`/applications/${$page.params.id}/configuration/database`, {
|
||||||
|
databaseId: database.id,
|
||||||
|
type: database.type
|
||||||
|
});
|
||||||
|
return window.location.assign(from || `/applications/${$page.params.id}/`);
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex space-x-1 p-6 font-bold">
|
||||||
|
<div class="mr-4 text-2xl tracking-tight">Select a Database</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-col justify-center mt-10 pb-12 sm:pb-16">
|
||||||
|
{#if !databases || ownDatabases.length === 0}
|
||||||
|
<div class="flex-col">
|
||||||
|
<div class="text-center text-xl font-bold">{$t('database.no_databases_found')}</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if ownDatabases.length > 0 || otherDatabases.length > 0}
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||||
|
{#each ownDatabases as database}
|
||||||
|
<button on:click={() => addCoolifyDatabase(database)} class="p-2 no-underline">
|
||||||
|
<div class="box-selection group relative hover:bg-purple-600">
|
||||||
|
<DatabaseIcons type={database.type} isAbsolute={true} />
|
||||||
|
<div class="truncate text-center text-xl font-bold">
|
||||||
|
{database.name}
|
||||||
|
</div>
|
||||||
|
{#if $appSession.teamId === '0' && otherDatabases.length > 0}
|
||||||
|
<div class="truncate text-center">{database.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
|
{#if database.destinationDocker?.name}
|
||||||
|
<div class="truncate text-center">{database.destinationDocker.name}</div>
|
||||||
|
{/if}
|
||||||
|
{#if !database.type}
|
||||||
|
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
||||||
|
{$t('application.configuration.configuration_missing')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{#if otherDatabases.length > 0 && $appSession.teamId === '0'}
|
||||||
|
<div class="px-6 pb-5 pt-10 text-2xl font-bold text-center">Other Databases</div>
|
||||||
|
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||||
|
{#each otherDatabases as database}
|
||||||
|
<a href="/databases/{database.id}" class="p-2 no-underline">
|
||||||
|
<div class="box-selection group relative hover:bg-purple-600">
|
||||||
|
<DatabaseIcons type={database.type} isAbsolute={true} />
|
||||||
|
<div class="truncate text-center text-xl font-bold">
|
||||||
|
{database.name}
|
||||||
|
</div>
|
||||||
|
{#if $appSession.teamId === '0'}
|
||||||
|
<div class="truncate text-center">{database.teams[0].name}</div>
|
||||||
|
{/if}
|
||||||
|
{#if !database.type}
|
||||||
|
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
|
||||||
|
Configuration missing
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="text-center truncate">{database.type}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-4xl p-6">
|
||||||
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
|
<div class="font-bold text-xl tracking-tight">Connect a Hosted / Remote Database</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
|
<input name="name" id="name" required bind:value={remoteDatabase.name} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="type" class="text-base font-bold text-stone-100">Type</label>
|
||||||
|
<input name="type" id="type" required bind:value={remoteDatabase.type} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="host" class="text-base font-bold text-stone-100">Host</label>
|
||||||
|
<input name="host" id="host" required bind:value={remoteDatabase.host} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="port" class="text-base font-bold text-stone-100">Port</label>
|
||||||
|
<input name="port" id="port" required bind:value={remoteDatabase.port} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="user" class="text-base font-bold text-stone-100">User</label>
|
||||||
|
<input name="user" id="user" required bind:value={remoteDatabase.user} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="password" class="text-base font-bold text-stone-100">Password</label>
|
||||||
|
<input name="password" id="password" required bind:value={remoteDatabase.password} />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-4">
|
||||||
|
<label for="database" class="text-base font-bold text-stone-100">Database Name</label>
|
||||||
|
<input name="database" id="database" required bind:value={remoteDatabase.database} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -31,15 +31,23 @@
|
|||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import Select from 'svelte-select';
|
import Select from 'svelte-select';
|
||||||
|
|
||||||
import { get, post } from '$lib/api';
|
import { get, post } from '$lib/api';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
import { addToast, appSession, disabledButton, setLocation, status } from '$lib/store';
|
import {
|
||||||
|
addToast,
|
||||||
|
appSession,
|
||||||
|
checkIfDeploymentEnabledApplications,
|
||||||
|
setLocation,
|
||||||
|
status,
|
||||||
|
isDeploymentEnabled,
|
||||||
|
features
|
||||||
|
} from '$lib/store';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common';
|
import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
import Explainer from '$lib/components/Explainer.svelte';
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
@@ -64,7 +72,9 @@
|
|||||||
let dualCerts = application.settings.dualCerts;
|
let dualCerts = application.settings.dualCerts;
|
||||||
let autodeploy = application.settings.autodeploy;
|
let autodeploy = application.settings.autodeploy;
|
||||||
let isBot = application.settings.isBot;
|
let isBot = application.settings.isBot;
|
||||||
|
let isDBBranching = application.settings.isDBBranching;
|
||||||
|
|
||||||
|
let baseDatabaseBranch: any = application?.connectedDatabase?.hostedDatabaseDBName || null;
|
||||||
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
let isNonWWWDomainOK = false;
|
let isNonWWWDomainOK = false;
|
||||||
let isWWWDomainOK = false;
|
let isWWWDomainOK = false;
|
||||||
@@ -162,6 +172,9 @@
|
|||||||
application.settings.isBot = isBot;
|
application.settings.isBot = isBot;
|
||||||
setLocation(application, settings);
|
setLocation(application, settings);
|
||||||
}
|
}
|
||||||
|
if (name === 'isDBBranching') {
|
||||||
|
isDBBranching = !isDBBranching;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await post(`/applications/${id}/settings`, {
|
await post(`/applications/${id}/settings`, {
|
||||||
previews,
|
previews,
|
||||||
@@ -169,6 +182,7 @@
|
|||||||
dualCerts,
|
dualCerts,
|
||||||
isBot,
|
isBot,
|
||||||
autodeploy,
|
autodeploy,
|
||||||
|
isDBBranching,
|
||||||
branch: application.branch,
|
branch: application.branch,
|
||||||
projectId: application.projectId
|
projectId: application.projectId
|
||||||
});
|
});
|
||||||
@@ -192,14 +206,20 @@
|
|||||||
if (name === 'isBot') {
|
if (name === 'isBot') {
|
||||||
isBot = !isBot;
|
isBot = !isBot;
|
||||||
}
|
}
|
||||||
|
if (name === 'isDBBranching') {
|
||||||
|
isDBBranching = !isDBBranching;
|
||||||
|
}
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (loading || (!application.fqdn && !isBot)) return;
|
if (loading) return;
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||||
|
console.log({debug: nonWWWDomain})
|
||||||
if (application.deploymentType)
|
if (application.deploymentType)
|
||||||
application.deploymentType = application.deploymentType.toLowerCase();
|
application.deploymentType = application.deploymentType.toLowerCase();
|
||||||
!isBot &&
|
!isBot &&
|
||||||
@@ -209,16 +229,17 @@
|
|||||||
dualCerts,
|
dualCerts,
|
||||||
exposePort: application.exposePort
|
exposePort: application.exposePort
|
||||||
}));
|
}));
|
||||||
await post(`/applications/${id}`, { ...application });
|
await post(`/applications/${id}`, { ...application, baseDatabaseBranch });
|
||||||
setLocation(application, settings);
|
setLocation(application, settings);
|
||||||
$disabledButton = false;
|
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||||
|
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
|
|
||||||
addToast({
|
addToast({
|
||||||
message: 'Configuration saved.',
|
message: 'Configuration saved.',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (error?.message.startsWith($t('application.dns_not_set_partial_error'))) {
|
if (error?.message.startsWith($t('application.dns_not_set_partial_error'))) {
|
||||||
forceSave = true;
|
forceSave = true;
|
||||||
@@ -509,6 +530,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $features.beta}
|
||||||
|
{#if !application.settings.isBot && !application.settings.isPublicRepository}
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<Setting
|
||||||
|
id="isDBBranching"
|
||||||
|
isCenter={false}
|
||||||
|
bind:setting={isDBBranching}
|
||||||
|
on:click={() => changeSettings('isDBBranching')}
|
||||||
|
title="Enable DB Branching"
|
||||||
|
description="Enable DB Branching"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if isDBBranching}
|
||||||
|
<button
|
||||||
|
on:click|stopPropagation|preventDefault={() =>
|
||||||
|
goto(`/applications/${id}/configuration/database`)}
|
||||||
|
class="btn btn-sm">Configure Connected Database</button
|
||||||
|
>
|
||||||
|
{#if application.connectedDatabase}
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<label for="baseImage" class="text-base font-bold text-stone-100"
|
||||||
|
>Base Database
|
||||||
|
<Explainer
|
||||||
|
explanation={'The name of the database that will be used as base when branching.'}
|
||||||
|
/></label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
name="baseDatabaseBranch"
|
||||||
|
required
|
||||||
|
id="baseDatabaseBranch"
|
||||||
|
bind:value={baseDatabaseBranch}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="text-center bg-green-600 rounded">
|
||||||
|
Connected to {application.connectedDatabase.databaseId}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-1 py-5 font-bold">
|
<div class="flex space-x-1 py-5 font-bold">
|
||||||
<div class="title">{$t('application.application')}</div>
|
<div class="title">{$t('application.application')}</div>
|
||||||
@@ -538,7 +599,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if !isBot}
|
{#if !isBot}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center pb-8">
|
||||||
<label for="fqdn" class="text-base font-bold text-stone-100"
|
<label for="fqdn" class="text-base font-bold text-stone-100"
|
||||||
>{$t('application.url_fqdn')}
|
>{$t('application.url_fqdn')}
|
||||||
<Explainer
|
<Explainer
|
||||||
@@ -551,7 +612,6 @@
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
name="fqdn"
|
name="fqdn"
|
||||||
id="fqdn"
|
id="fqdn"
|
||||||
required
|
|
||||||
bind:value={application.fqdn}
|
bind:value={application.fqdn}
|
||||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||||
placeholder="eg: https://coollabs.io"
|
placeholder="eg: https://coollabs.io"
|
||||||
@@ -658,7 +718,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center pb-8">
|
||||||
<label for="exposePort" class="text-base font-bold text-stone-100"
|
<label for="exposePort" class="text-base font-bold text-stone-100"
|
||||||
>Exposed Port <Explainer
|
>Exposed Port <Explainer
|
||||||
explanation={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
explanation={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
|
||||||
@@ -700,7 +760,7 @@
|
|||||||
placeholder="{$t('forms.default')}: yarn build"
|
placeholder="{$t('forms.default')}: yarn build"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center pb-8">
|
||||||
<label for="startCommand" class="text-base font-bold text-stone-100"
|
<label for="startCommand" class="text-base font-bold text-stone-100"
|
||||||
>{$t('application.start_command')}</label
|
>{$t('application.start_command')}</label
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -67,7 +67,6 @@
|
|||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +79,6 @@
|
|||||||
applicationId: id
|
applicationId: id
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@
|
|||||||
logs = data.logs;
|
logs = data.logs;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
logsLoading = false;
|
logsLoading = false;
|
||||||
|
|||||||
@@ -87,12 +87,15 @@
|
|||||||
const sure = confirm($t('database.confirm_stop', { name: database.name }));
|
const sure = confirm($t('database.confirm_stop', { name: database.name }));
|
||||||
if (sure) {
|
if (sure) {
|
||||||
$status.database.initialLoading = true;
|
$status.database.initialLoading = true;
|
||||||
|
$status.database.loading = true;
|
||||||
try {
|
try {
|
||||||
await post(`/databases/${database.id}/stop`, {});
|
await post(`/databases/${database.id}/stop`, {});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
$status.database.initialLoading = false;
|
$status.database.initialLoading = false;
|
||||||
|
$status.database.loading = false;
|
||||||
|
await getStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +178,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if $status.database.initialLoading}
|
{#if $status.database.initialLoading}
|
||||||
<button
|
<button
|
||||||
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out hover:bg-transparent"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
logs = data.logs;
|
logs = data.logs;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
loadingLogs = false;
|
loadingLogs = false;
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
loading.proxy = true;
|
|
||||||
if (destination.remoteEngine && destination.remoteVerified) {
|
if (destination.remoteEngine && destination.remoteVerified) {
|
||||||
|
loading.proxy = true;
|
||||||
const { isRunning } = await get(`/destinations/${id}/status`);
|
const { isRunning } = await get(`/destinations/${id}/status`);
|
||||||
if (isRunning === false && destination.isCoolifyProxyUsed === true) {
|
if (isRunning === false && destination.isCoolifyProxyUsed === true) {
|
||||||
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
@@ -69,6 +69,7 @@
|
|||||||
loading.proxy = false;
|
loading.proxy = false;
|
||||||
});
|
});
|
||||||
async function changeProxySetting() {
|
async function changeProxySetting() {
|
||||||
|
if (!destination.remoteVerified) return
|
||||||
loading.proxy = true;
|
loading.proxy = true;
|
||||||
if (!cannotDisable) {
|
if (!cannotDisable) {
|
||||||
const isProxyActivated = destination.isCoolifyProxyUsed;
|
const isProxyActivated = destination.isCoolifyProxyUsed;
|
||||||
@@ -262,7 +263,7 @@
|
|||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<Setting
|
<Setting
|
||||||
id="changeProxySetting"
|
id="changeProxySetting"
|
||||||
disabled={cannotDisable}
|
disabled={cannotDisable || !destination.remoteVerified}
|
||||||
loading={loading.proxy}
|
loading={loading.proxy}
|
||||||
bind:setting={destination.isCoolifyProxyUsed}
|
bind:setting={destination.isCoolifyProxyUsed}
|
||||||
on:click={changeProxySetting}
|
on:click={changeProxySetting}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return handlerNotFoundLoad(error, url);
|
return handlerNotFoundLoad(error, url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -58,11 +57,11 @@
|
|||||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
|
|
||||||
const { id } = $page.params;
|
|
||||||
const isDestinationDeletable =
|
const isDestinationDeletable =
|
||||||
destination?.application.length === 0 &&
|
(destination?.application.length === 0 &&
|
||||||
destination?.database.length === 0 &&
|
destination?.database.length === 0 &&
|
||||||
destination?.service.length === 0;
|
destination?.service.length === 0) ||
|
||||||
|
true;
|
||||||
|
|
||||||
async function deleteDestination(destination: any) {
|
async function deleteDestination(destination: any) {
|
||||||
if (!isDestinationDeletable) return;
|
if (!isDestinationDeletable) return;
|
||||||
@@ -88,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if id !== 'new'}
|
{#if $page.params.id !== 'new'}
|
||||||
<nav class="nav-side">
|
<nav class="nav-side">
|
||||||
<button
|
<button
|
||||||
id="delete"
|
id="delete"
|
||||||
|
|||||||
@@ -49,61 +49,83 @@
|
|||||||
<title>{$t('login.login')}</title>
|
<title>{$t('login.login')}</title>
|
||||||
</svelt:head>
|
</svelt:head>
|
||||||
|
|
||||||
<div class="flex h-screen flex-col items-center justify-center">
|
<div class="flex lg:flex-row flex-col h-screen">
|
||||||
<div class="flex justify-center px-4">
|
<div class="bg-neutral-focus h-screen lg:flex hidden flex-col justify-end p-20 flex-1">
|
||||||
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
|
<h1 class="title lg:text-6xl mb-5 border-gradient">Coolify</h1>
|
||||||
{#if $appSession.whiteLabeledDetails.icon}
|
<h3 class="title">Made self-hosting simple.</h3>
|
||||||
<img
|
|
||||||
class="w-32 mx-auto pb-8"
|
|
||||||
src={$appSession.whiteLabeledDetails.icon}
|
|
||||||
alt="Icon for white labeled version of Coolify"
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<div class="text-6xl font-bold border-gradient w-48 mx-auto border-b-4 mb-8">Coolify</div>
|
|
||||||
{/if}
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
placeholder={$t('forms.email')}
|
|
||||||
autocomplete="off"
|
|
||||||
required
|
|
||||||
bind:this={emailEl}
|
|
||||||
bind:value={email}
|
|
||||||
class="w-56 md:w-96"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
placeholder={$t('forms.password')}
|
|
||||||
bind:value={password}
|
|
||||||
required
|
|
||||||
class="w-56 md:w-96"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={loading}
|
|
||||||
class="btn btn-sm"
|
|
||||||
class:loading
|
|
||||||
class:bg-coollabs={!loading}
|
|
||||||
>{loading ? $t('login.authenticating') : $t('login.login')}</button
|
|
||||||
>
|
|
||||||
|
|
||||||
<button on:click|preventDefault={gotoRegister} class="btn btn-sm"
|
|
||||||
>{$t('register.register')}</button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{#if browser && window.location.host === 'demo.coolify.io'}
|
<div class="flex flex-1 flex-col lg:max-w-2xl">
|
||||||
<div class="pt-5 font-bold">
|
<div class="flex flex-row p-8 items-center space-x-3">
|
||||||
Registration is <span class="text-pink-500">open</span>, just fill in an email (does not need
|
{#if $appSession.whiteLabeledDetails.icon}
|
||||||
to be live email address for the demo instance) and a password.
|
<div class="avatar" style="width: 40px; height: 40px">
|
||||||
|
<img
|
||||||
|
src={$appSession.whiteLabeledDetails.icon}
|
||||||
|
alt="Icon for white labeled version of Coolify"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div>
|
||||||
|
<div class="avatar" style="width: 40px; height: 40px">
|
||||||
|
<img src="favicon.png" alt="Coolify icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="prose">
|
||||||
|
<h4>Coolify</h4>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-5 font-bold">
|
<div
|
||||||
All users gets an <span class="text-pink-500">own namespace</span>, so you won't be able to
|
class="w-full md:px-20 lg:px-10 xl:px-20 p-6 flex flex-col h-full justify-center items-center"
|
||||||
access other users data.
|
>
|
||||||
|
<div class="mb-5 w-full prose prose-neutral">
|
||||||
|
<h1 class="m-0 white">Welcome back</h1>
|
||||||
|
<h5>Please login to continue.</h5>
|
||||||
|
</div>
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-3 w-full">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
placeholder={$t('forms.email')}
|
||||||
|
autocomplete="off"
|
||||||
|
required
|
||||||
|
bind:this={emailEl}
|
||||||
|
bind:value={email}
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
placeholder={$t('forms.password')}
|
||||||
|
bind:value={password}
|
||||||
|
required
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex space-y-3 flex-col pt-3">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
class="btn"
|
||||||
|
class:loading
|
||||||
|
class:bg-coollabs={!loading}
|
||||||
|
>{loading ? $t('login.authenticating') : $t('login.login')}</button
|
||||||
|
>
|
||||||
|
|
||||||
|
<button on:click|preventDefault={gotoRegister} class="btn btn-ghost"
|
||||||
|
>{$t('register.register')}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{#if browser && window.location.host === 'demo.coolify.io'}
|
||||||
|
<div class="pt-5 font-bold">
|
||||||
|
Registration is <span class="text-pink-500">open</span>, just fill in an email (does not
|
||||||
|
need to be live email address for the demo instance) and a password.
|
||||||
|
</div>
|
||||||
|
<div class="pt-5 font-bold">
|
||||||
|
All users gets an <span class="text-pink-500">own namespace</span>, so you won't be able
|
||||||
|
to access other users data.
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -61,38 +61,58 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="icons fixed top-0 left-0 m-3 cursor-pointer" on:click={() => goto('/')}>
|
<div class="flex lg:flex-row flex-col h-screen">
|
||||||
<svg
|
<div class="bg-neutral-focus h-screen lg:flex hidden flex-col justify-end p-20 flex-1">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<h1 class="title lg:text-6xl mb-5 border-gradient">Coolify</h1>
|
||||||
class="h-6 w-6"
|
<h3 class="title">Made self-hosting simple.</h3>
|
||||||
viewBox="0 0 24 24"
|
</div>
|
||||||
stroke-width="1.5"
|
<div class="flex flex-1 flex-col lg:max-w-2xl">
|
||||||
stroke="currentColor"
|
<div class="flex flex-row p-8 items-center space-x-3 justify-between">
|
||||||
fill="none"
|
<div class="icons cursor-pointer" on:click={() => goto('/')}>
|
||||||
stroke-linecap="round"
|
<svg
|
||||||
stroke-linejoin="round"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
class="h-6 w-6"
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
viewBox="0 0 24 24"
|
||||||
<line x1="5" y1="12" x2="19" y2="12" />
|
stroke-width="1.5"
|
||||||
<line x1="5" y1="12" x2="11" y2="18" />
|
stroke="currentColor"
|
||||||
<line x1="5" y1="12" x2="11" y2="6" />
|
fill="none"
|
||||||
</svg>
|
stroke-linecap="round"
|
||||||
</div>
|
stroke-linejoin="round"
|
||||||
<div class="flex h-screen flex-col items-center justify-center">
|
>
|
||||||
{#if $appSession.userId}
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<div class="flex justify-center px-4 text-xl font-bold">{$t('login.already_logged_in')}</div>
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
{:else}
|
<line x1="5" y1="12" x2="11" y2="18" />
|
||||||
<div class="flex justify-center px-4">
|
<line x1="5" y1="12" x2="11" y2="6" />
|
||||||
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-center space-x-3">
|
||||||
{#if $appSession.whiteLabeledDetails.icon}
|
{#if $appSession.whiteLabeledDetails.icon}
|
||||||
<img
|
<div class="avatar" style="width: 40px; height: 40px">
|
||||||
class="w-32 mx-auto pb-8"
|
<img
|
||||||
src={$appSession.whiteLabeledDetails.icon}
|
src={$appSession.whiteLabeledDetails.icon}
|
||||||
alt="Icon for white labeled version of Coolify"
|
alt="Icon for white labeled version of Coolify"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-6xl font-bold border-gradient w-48 mx-auto border-b-4 mb-8">Coolify</div>
|
<div>
|
||||||
|
<div class="avatar" style="width: 40px; height: 40px">
|
||||||
|
<img src="favicon.png" alt="Coolify icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="prose">
|
||||||
|
<h4>Coolify</h4>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full md:px-20 lg:px-10 xl:px-20 p-6 flex flex-col h-full justify-center items-center"
|
||||||
|
>
|
||||||
|
<div class="mb-5 w-full prose prose-neutral">
|
||||||
|
<h1 class="m-0 white">Get started</h1>
|
||||||
|
<h5>Enter the required fields to complete the registration.</h5>
|
||||||
|
</div>
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-3 w-full">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
@@ -101,7 +121,7 @@
|
|||||||
required
|
required
|
||||||
bind:this={emailEl}
|
bind:this={emailEl}
|
||||||
bind:value={email}
|
bind:value={email}
|
||||||
class="w-56 md:w-96"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -110,7 +130,7 @@
|
|||||||
bind:this={passwordEl}
|
bind:this={passwordEl}
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
required
|
required
|
||||||
class="w-56 md:w-96"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -118,13 +138,13 @@
|
|||||||
placeholder={$t('forms.password_again')}
|
placeholder={$t('forms.password_again')}
|
||||||
bind:value={passwordCheck}
|
bind:value={passwordCheck}
|
||||||
required
|
required
|
||||||
class="w-56 md:w-96"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
|
<div class="flex space-y-3 flex-col pt-3">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-sm"
|
class="btn"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
class:bg-transparent={loading}
|
class:bg-transparent={loading}
|
||||||
class:bg-coollabs={!loading}
|
class:bg-coollabs={!loading}
|
||||||
@@ -132,11 +152,11 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{#if userCount === 0}
|
||||||
|
<div class="pt-5">
|
||||||
|
{$t('register.first_user')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if userCount === 0}
|
</div>
|
||||||
<div class="pt-5">
|
|
||||||
{$t('register.first_user')}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,14 @@
|
|||||||
import { get, post } from '$lib/api';
|
import { get, post } from '$lib/api';
|
||||||
import { errorNotification, getDomain } from '$lib/common';
|
import { errorNotification, getDomain } from '$lib/common';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
|
import {
|
||||||
|
appSession,
|
||||||
|
status,
|
||||||
|
setLocation,
|
||||||
|
addToast,
|
||||||
|
checkIfDeploymentEnabledServices,
|
||||||
|
isDeploymentEnabled
|
||||||
|
} from '$lib/store';
|
||||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
|
||||||
@@ -78,8 +85,8 @@
|
|||||||
});
|
});
|
||||||
await post(`/services/${id}`, { ...service });
|
await post(`/services/${id}`, { ...service });
|
||||||
setLocation(service);
|
setLocation(service);
|
||||||
$disabledButton = false;
|
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
|
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||||
return addToast({
|
return addToast({
|
||||||
message: 'Configuration saved.',
|
message: 'Configuration saved.',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
export let readOnly: any;
|
export let readOnly: any;
|
||||||
export let settings: any;
|
export let settings: any;
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
const { ipv4, ipv6 } = settings;
|
||||||
let ftpUrl = generateUrl(service.wordpress.ftpPublicPort);
|
let ftpUrl = generateUrl(service.wordpress.ftpPublicPort);
|
||||||
let ftpUser = service.wordpress.ftpUser;
|
let ftpUser = service.wordpress.ftpUser;
|
||||||
let ftpPassword = service.wordpress.ftpPassword;
|
let ftpPassword = service.wordpress.ftpPassword;
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
function generateUrl(publicPort: any) {
|
function generateUrl(publicPort: any) {
|
||||||
return browser
|
return browser
|
||||||
? `sftp://${
|
? `sftp://${
|
||||||
settings?.fqdn ? getDomain(settings.fqdn) : window.location.hostname
|
settings?.fqdn ? getDomain(settings.fqdn) : ipv4 || ipv6
|
||||||
}:${publicPort}`
|
}:${publicPort}`
|
||||||
: 'Loading...';
|
: 'Loading...';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return handlerNotFoundLoad(error, url);
|
return handlerNotFoundLoad(error, url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -60,19 +59,21 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
||||||
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
|
import {
|
||||||
|
appSession,
|
||||||
|
isDeploymentEnabled,
|
||||||
|
status,
|
||||||
|
location,
|
||||||
|
setLocation,
|
||||||
|
checkIfDeploymentEnabledServices
|
||||||
|
} from '$lib/store';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
const { id } = $page.params;
|
const { id } = $page.params;
|
||||||
|
|
||||||
export let service: any;
|
export let service: any;
|
||||||
|
|
||||||
$disabledButton =
|
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||||
!$appSession.isAdmin ||
|
|
||||||
!service.fqdn ||
|
|
||||||
!service.destinationDocker ||
|
|
||||||
!service.version ||
|
|
||||||
!service.type;
|
|
||||||
|
|
||||||
let statusInterval: any;
|
let statusInterval: any;
|
||||||
|
|
||||||
@@ -96,12 +97,15 @@
|
|||||||
const sure = confirm($t('database.confirm_stop', { name: service.name }));
|
const sure = confirm($t('database.confirm_stop', { name: service.name }));
|
||||||
if (sure) {
|
if (sure) {
|
||||||
$status.service.initialLoading = true;
|
$status.service.initialLoading = true;
|
||||||
|
$status.service.loading = true;
|
||||||
try {
|
try {
|
||||||
await post(`/services/${service.id}/${service.type}/stop`, {});
|
await post(`/services/${service.id}/${service.type}/stop`, {});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
$status.service.initialLoading = false;
|
$status.service.initialLoading = false;
|
||||||
|
$status.service.loading = false;
|
||||||
|
await getStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,6 +137,7 @@
|
|||||||
$status.service.isExited = false;
|
$status.service.isExited = false;
|
||||||
$status.service.loading = false;
|
$status.service.loading = false;
|
||||||
$location = null;
|
$location = null;
|
||||||
|
$isDeploymentEnabled = false;
|
||||||
clearInterval(statusInterval);
|
clearInterval(statusInterval);
|
||||||
});
|
});
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -151,141 +156,142 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="nav-side">
|
<nav class="nav-side">
|
||||||
{#if service.type && service.destinationDockerId && service.version && service.fqdn}
|
{#if $location}
|
||||||
{#if $location}
|
<a
|
||||||
<a
|
id="open"
|
||||||
id="open"
|
href={$location}
|
||||||
href={$location}
|
target="_blank"
|
||||||
target="_blank"
|
class="icons flex items-center bg-transparent text-sm"
|
||||||
class="icons flex items-center bg-transparent text-sm"
|
><svg
|
||||||
><svg
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
class="h-6 w-6"
|
||||||
class="h-6 w-6"
|
viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"
|
stroke-width="1.5"
|
||||||
stroke-width="1.5"
|
stroke="currentColor"
|
||||||
stroke="currentColor"
|
fill="none"
|
||||||
fill="none"
|
stroke-linecap="round"
|
||||||
stroke-linecap="round"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
|
||||||
<line x1="10" y1="14" x2="20" y2="4" />
|
|
||||||
<polyline points="15 4 20 4 20 9" />
|
|
||||||
</svg></a
|
|
||||||
>
|
>
|
||||||
<Tooltip triggeredBy="#open">Open</Tooltip>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<div class="border border-stone-700 h-8" />
|
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||||
{/if}
|
<line x1="10" y1="14" x2="20" y2="4" />
|
||||||
{#if $status.service.isExited}
|
<polyline points="15 4 20 4 20 9" />
|
||||||
<a
|
</svg></a
|
||||||
id="error"
|
>
|
||||||
href={!$disabledButton ? `/services/${id}/logs` : null}
|
<Tooltip triggeredBy="#open">Open</Tooltip>
|
||||||
class="icons bg-transparent text-sm flex items-center text-red-500 tooltip-error"
|
|
||||||
sveltekit:prefetch
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="w-6 h-6"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentcolor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path
|
|
||||||
d="M8.7 3h6.6c.3 0 .5 .1 .7 .3l4.7 4.7c.2 .2 .3 .4 .3 .7v6.6c0 .3 -.1 .5 -.3 .7l-4.7 4.7c-.2 .2 -.4 .3 -.7 .3h-6.6c-.3 0 -.5 -.1 -.7 -.3l-4.7 -4.7c-.2 -.2 -.3 -.4 -.3 -.7v-6.6c0 -.3 .1 -.5 .3 -.7l4.7 -4.7c.2 -.2 .4 -.3 .7 -.3z"
|
|
||||||
/>
|
|
||||||
<line x1="12" y1="8" x2="12" y2="12" />
|
|
||||||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<Tooltip triggeredBy="#error">Service exited with an error!</Tooltip>
|
|
||||||
{/if}
|
|
||||||
{#if $status.service.initialLoading}
|
|
||||||
<button
|
|
||||||
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
|
|
||||||
<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
|
|
||||||
<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
|
|
||||||
<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
|
|
||||||
<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
|
|
||||||
<line x1="11" y1="19.94" x2="11" y2="19.95" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{:else if $status.service.isRunning}
|
|
||||||
<button
|
|
||||||
id="stop"
|
|
||||||
on:click={stopService}
|
|
||||||
type="submit"
|
|
||||||
disabled={$disabledButton}
|
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2 text-red-500"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="w-6 h-6"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
|
||||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Tooltip triggeredBy="#stop">Stop</Tooltip>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
id="start"
|
|
||||||
on:click={startService}
|
|
||||||
type="submit"
|
|
||||||
disabled={$disabledButton}
|
|
||||||
class="icons bg-transparent text-sm flex items-center space-x-2 text-green-500"
|
|
||||||
><svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="w-6 h-6"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M7 4v16l13 -8z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Tooltip triggeredBy="#start">Start</Tooltip>
|
|
||||||
{/if}
|
|
||||||
<div class="border border-stone-700 h-8" />
|
<div class="border border-stone-700 h-8" />
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $status.service.isExited}
|
||||||
|
<a
|
||||||
|
id="error"
|
||||||
|
href={$isDeploymentEnabled ? `/services/${id}/logs` : null}
|
||||||
|
class="icons bg-transparent text-sm flex items-center text-red-500 tooltip-error"
|
||||||
|
sveltekit:prefetch
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentcolor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path
|
||||||
|
d="M8.7 3h6.6c.3 0 .5 .1 .7 .3l4.7 4.7c.2 .2 .3 .4 .3 .7v6.6c0 .3 -.1 .5 -.3 .7l-4.7 4.7c-.2 .2 -.4 .3 -.7 .3h-6.6c-.3 0 -.5 -.1 -.7 -.3l-4.7 -4.7c-.2 -.2 -.3 -.4 -.3 -.7v-6.6c0 -.3 .1 -.5 .3 -.7l4.7 -4.7c.2 -.2 .4 -.3 .7 -.3z"
|
||||||
|
/>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="12" />
|
||||||
|
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<Tooltip triggeredBy="#error">Service exited with an error!</Tooltip>
|
||||||
|
{/if}
|
||||||
|
{#if $status.service.initialLoading}
|
||||||
|
<button
|
||||||
|
class="icons flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
|
||||||
|
<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
|
||||||
|
<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
|
||||||
|
<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
|
||||||
|
<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
|
||||||
|
<line x1="11" y1="19.94" x2="11" y2="19.95" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{:else if $status.service.isRunning}
|
||||||
|
<button
|
||||||
|
id="stop"
|
||||||
|
on:click={stopService}
|
||||||
|
type="submit"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm flex items-center space-x-2 text-red-500"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||||
|
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<Tooltip triggeredBy="#stop">Stop</Tooltip>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
id="start"
|
||||||
|
on:click={startService}
|
||||||
|
type="submit"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm flex items-center space-x-2 text-green-500"
|
||||||
|
><svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-6 h-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M7 4v16l13 -8z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<Tooltip triggeredBy="#start">Start</Tooltip>
|
||||||
|
{/if}
|
||||||
|
<div class="border border-stone-700 h-8" />
|
||||||
{#if service.type && service.destinationDockerId && service.version}
|
{#if service.type && service.destinationDockerId && service.version}
|
||||||
<a
|
<a
|
||||||
id="configuration"
|
|
||||||
href="/services/{id}"
|
href="/services/{id}"
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-yellow-500 rounded"
|
class="hover:text-yellow-500 rounded"
|
||||||
class:text-yellow-500={$page.url.pathname === `/services/${id}`}
|
class:text-yellow-500={$page.url.pathname === `/services/${id}`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/services/${id}`}
|
class:bg-coolgray-500={$page.url.pathname === `/services/${id}`}
|
||||||
>
|
>
|
||||||
<button class="icons bg-transparent text-sm disabled:text-red-500">
|
<button
|
||||||
|
id="configuration"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
@@ -311,14 +317,17 @@
|
|||||||
>
|
>
|
||||||
<Tooltip triggeredBy="#configuration">Configuration</Tooltip>
|
<Tooltip triggeredBy="#configuration">Configuration</Tooltip>
|
||||||
<a
|
<a
|
||||||
id="secrets"
|
|
||||||
href="/services/{id}/secrets"
|
href="/services/{id}/secrets"
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-pink-500 rounded"
|
class="hover:text-pink-500 rounded"
|
||||||
class:text-pink-500={$page.url.pathname === `/services/${id}/secrets`}
|
class:text-pink-500={$page.url.pathname === `/services/${id}/secrets`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/secrets`}
|
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/secrets`}
|
||||||
>
|
>
|
||||||
<button class="icons bg-transparent text-sm disabled:text-red-500">
|
<button
|
||||||
|
id="secrets"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm "
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
@@ -340,14 +349,17 @@
|
|||||||
>
|
>
|
||||||
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
|
<Tooltip triggeredBy="#secrets">Secrets</Tooltip>
|
||||||
<a
|
<a
|
||||||
id="persistentstorage"
|
|
||||||
href="/services/{id}/storages"
|
href="/services/{id}/storages"
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-pink-500 rounded"
|
class="hover:text-pink-500 rounded"
|
||||||
class:text-pink-500={$page.url.pathname === `/services/${id}/storages`}
|
class:text-pink-500={$page.url.pathname === `/services/${id}/storages`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/storages`}
|
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/storages`}
|
||||||
>
|
>
|
||||||
<button class="icons bg-transparent text-sm disabled:text-red-500">
|
<button
|
||||||
|
id="persistentstorage"
|
||||||
|
disabled={!$isDeploymentEnabled}
|
||||||
|
class="icons bg-transparent text-sm"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
@@ -368,14 +380,13 @@
|
|||||||
<Tooltip triggeredBy="#persistentstorage">Persistent Storage</Tooltip>
|
<Tooltip triggeredBy="#persistentstorage">Persistent Storage</Tooltip>
|
||||||
<div class="border border-stone-700 h-8" />
|
<div class="border border-stone-700 h-8" />
|
||||||
<a
|
<a
|
||||||
id="logs"
|
href={$isDeploymentEnabled && $status.service.isRunning ? `/services/${id}/logs` : null}
|
||||||
href={!$disabledButton && $status.service.isRunning ? `/services/${id}/logs` : null}
|
|
||||||
sveltekit:prefetch
|
sveltekit:prefetch
|
||||||
class="hover:text-pink-500 rounded"
|
class="hover:text-pink-500 rounded"
|
||||||
class:text-pink-500={$page.url.pathname === `/services/${id}/logs`}
|
class:text-pink-500={$page.url.pathname === `/services/${id}/logs`}
|
||||||
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/logs`}
|
class:bg-coolgray-500={$page.url.pathname === `/services/${id}/logs`}
|
||||||
>
|
>
|
||||||
<button disabled={!$status.service.isRunning} class="icons bg-transparent text-sm">
|
<button id="logs" disabled={!$status.service.isRunning} class="icons bg-transparent text-sm">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-6 w-6"
|
class="h-6 w-6"
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
logs = data.logs;
|
logs = data.logs;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
logsLoading = false;
|
logsLoading = false;
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { addToast, appSession, features } from '$lib/store';
|
import { addToast, appSession, features } from '$lib/store';
|
||||||
import { errorNotification, getDomain } from '$lib/common';
|
import { asyncSleep, errorNotification, getDomain } from '$lib/common';
|
||||||
import Menu from './_Menu.svelte';
|
import Menu from './_Menu.svelte';
|
||||||
import Explainer from '$lib/components/Explainer.svelte';
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
|
||||||
|
let isAPIDebuggingEnabled = settings.isAPIDebuggingEnabled;
|
||||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||||
let dualCerts = settings.dualCerts;
|
let dualCerts = settings.dualCerts;
|
||||||
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
let loading = {
|
let loading = {
|
||||||
save: false,
|
save: false,
|
||||||
remove: false,
|
remove: false,
|
||||||
proxyMigration: false
|
proxyMigration: false,
|
||||||
|
restart: false
|
||||||
};
|
};
|
||||||
|
|
||||||
async function removeFqdn() {
|
async function removeFqdn() {
|
||||||
@@ -75,8 +77,11 @@
|
|||||||
if (name === 'isDNSCheckEnabled') {
|
if (name === 'isDNSCheckEnabled') {
|
||||||
isDNSCheckEnabled = !isDNSCheckEnabled;
|
isDNSCheckEnabled = !isDNSCheckEnabled;
|
||||||
}
|
}
|
||||||
|
if (name === 'isAPIDebuggingEnabled') {
|
||||||
|
isAPIDebuggingEnabled = !isAPIDebuggingEnabled;
|
||||||
|
}
|
||||||
await post(`/settings`, {
|
await post(`/settings`, {
|
||||||
|
isAPIDebuggingEnabled,
|
||||||
isRegistrationEnabled,
|
isRegistrationEnabled,
|
||||||
dualCerts,
|
dualCerts,
|
||||||
isAutoUpdateEnabled,
|
isAutoUpdateEnabled,
|
||||||
@@ -129,7 +134,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(error);
|
|
||||||
return errorNotification(error);
|
return errorNotification(error);
|
||||||
} finally {
|
} finally {
|
||||||
loading.save = false;
|
loading.save = false;
|
||||||
@@ -153,6 +157,41 @@
|
|||||||
function resetView() {
|
function resetView() {
|
||||||
forceSave = false;
|
forceSave = false;
|
||||||
}
|
}
|
||||||
|
async function restartCoolify() {
|
||||||
|
const sure = confirm(
|
||||||
|
'Are you sure you would like to restart Coolify? Currently running deployments will be stopped and restarted.'
|
||||||
|
);
|
||||||
|
if (sure) {
|
||||||
|
loading.restart = true;
|
||||||
|
try {
|
||||||
|
await post(`/internal/restart`, {});
|
||||||
|
await asyncSleep(10000);
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
await asyncSleep(3000);
|
||||||
|
return window.location.reload();
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading.restart = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 font-bold">
|
<div class="flex space-x-1 p-6 font-bold">
|
||||||
@@ -183,11 +222,14 @@
|
|||||||
on:click|preventDefault={removeFqdn}
|
on:click|preventDefault={removeFqdn}
|
||||||
disabled={loading.remove}
|
disabled={loading.remove}
|
||||||
class="btn btn-sm"
|
class="btn btn-sm"
|
||||||
class:bg-red-600={!loading.remove}
|
|
||||||
class:hover:bg-red-500={!loading.remove}
|
|
||||||
>{loading.remove ? $t('forms.removing') : $t('forms.remove_domain')}</button
|
>{loading.remove ? $t('forms.removing') : $t('forms.remove_domain')}</button
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
<button
|
||||||
|
on:click={restartCoolify}
|
||||||
|
class:loading={loading.restart}
|
||||||
|
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-row gap-2 px-10">
|
<div class="grid grid-flow-row gap-2 px-10">
|
||||||
<!-- <Language /> -->
|
<!-- <Language /> -->
|
||||||
@@ -311,17 +353,24 @@
|
|||||||
on:click={() => changeSettings('isRegistrationEnabled')}
|
on:click={() => changeSettings('isRegistrationEnabled')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if browser && $features.beta}
|
<div class="grid grid-cols-2 items-center">
|
||||||
<div class="grid grid-cols-2 items-center">
|
<Setting
|
||||||
<Setting
|
id="isAPIDebuggingEnabled"
|
||||||
id="isAutoUpdateEnabled"
|
bind:setting={isAPIDebuggingEnabled}
|
||||||
bind:setting={isAutoUpdateEnabled}
|
title="API Debugging"
|
||||||
title={$t('setting.auto_update_enabled')}
|
description="Enable API debugging. This will log all API requests and responses.<br><br>You need to restart the Coolify for this to take effect."
|
||||||
description={$t('setting.auto_update_enabled_explainer')}
|
on:click={() => changeSettings('isAPIDebuggingEnabled')}
|
||||||
on:click={() => changeSettings('isAutoUpdateEnabled')}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div class="grid grid-cols-2 items-center">
|
||||||
{/if}
|
<Setting
|
||||||
|
id="isAutoUpdateEnabled"
|
||||||
|
bind:setting={isAutoUpdateEnabled}
|
||||||
|
title={$t('setting.auto_update_enabled')}
|
||||||
|
description={$t('setting.auto_update_enabled_explainer')}
|
||||||
|
on:click={() => changeSettings('isAutoUpdateEnabled')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,8 +46,8 @@
|
|||||||
customPort: source.customPort
|
customPort: source.customPort
|
||||||
});
|
});
|
||||||
const { organization, htmlUrl } = source;
|
const { organization, htmlUrl } = source;
|
||||||
const { fqdn } = settings;
|
const { fqdn, ipv4, ipv6 } = settings;
|
||||||
const host = dev ? getAPIUrl() : fqdn ? fqdn : `http://${window.location.host}` || '';
|
const host = dev ? getAPIUrl() : fqdn ? fqdn : `http://${ipv4 || ipv6}` || '';
|
||||||
const domain = getDomain(fqdn);
|
const domain = getDomain(fqdn);
|
||||||
|
|
||||||
let url = 'settings/apps/new';
|
let url = 'settings/apps/new';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "coolify",
|
"name": "coolify",
|
||||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||||
"version": "3.9.0-rc.1",
|
"version": "3.9.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": "github:coollabsio/coolify",
|
"repository": "github:coollabsio/coolify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
903
pnpm-lock.yaml
generated
903
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user