diff --git a/.github/workflows/production-release-dockerhub.yml b/.github/workflows/production-release-dockerhub.yml deleted file mode 100644 index 1a1adaad4..000000000 --- a/.github/workflows/production-release-dockerhub.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Production Release to DockerHub - -on: - push: - branches: - - "this-branch-does-not-exists" - -jobs: - arm64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/arm64 - push: true - tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 - cache-from: type=registry,ref=coollabsio/coolify:buildcache-arm64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-arm64,mode=max - amd64: - 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}} - cache-from: type=registry,ref=coollabsio/coolify:buildcache-amd64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-amd64,mode=max - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/aarch64 - push: true - tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 - cache-from: type=registry,ref=coollabsio/coolify:buildcache-aarch64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-aarch64,mode=max - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, arm64, aarch64] - 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 buildx imagetools create --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 --tag coollabsio/coolify:${{steps.package-version.outputs.current-version}} - docker buildx imagetools create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --tag coollabsio/coolify:latest - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }} diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml index 1adc0cb47..0ab618968 100644 --- a/.github/workflows/production-release.yml +++ b/.github/workflows/production-release.yml @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: "v3" - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx @@ -44,6 +46,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: "v3" - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx @@ -95,7 +99,6 @@ jobs: - name: Create & publish manifest run: | docker buildx imagetools create --append ${{ fromJSON(steps.meta.outputs.json).tags[0] }}-aarch64 --tag ${{ fromJSON(steps.meta.outputs.json).tags[0] }} - docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest - uses: sarisia/actions-status-discord@v1 if: always() with: diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml deleted file mode 100644 index e69dd009c..000000000 --- a/.github/workflows/release-candidate.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Release Candidate to ghcr.io - -on: - release: - types: [prereleased] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: "coollabsio/coolify" - -jobs: - amd64: - 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 ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/aarch64 - push: true - tags: ${{ steps.meta.outputs.tags }}-aarch64 - labels: ${{ steps.meta.outputs.labels }} - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, aarch64] - 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 ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Create & publish manifest - run: | - docker buildx imagetools create --append ${{ steps.meta.outputs.tags }}-aarch64 --tag ${{ steps.meta.outputs.tags }} - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.github/workflows/staging-release-dockerhub.yml b/.github/workflows/staging-release-dockerhub.yml deleted file mode 100644 index 111cd54a0..000000000 --- a/.github/workflows/staging-release-dockerhub.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Staging Release to DockerHub - -on: - push: - branches: - - "this-branch-does-not-exists" - -jobs: - arm64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: "next" - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/arm64 - push: true - tags: coollabsio/coolify: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: - 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:next - cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-amd64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max - merge-manifest: - runs-on: ubuntu-latest - needs: [arm64, amd64] - 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 buildx imagetools create --append coollabsio/coolify:next-arm64 --tag coollabsio/coolify:next - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index dfd96e9bb..09248632d 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -18,7 +18,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: "next" + ref: "v3" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to ghcr.io @@ -47,7 +47,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: "next" + ref: "v3" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to ghcr.io diff --git a/Dockerfile b/Dockerfile index 80f56f06a..e45a29852 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ ARG DOCKER_COMPOSE_VERSION=2.6.1 # https://github.com/buildpacks/pack/releases ARG PACK_VERSION=0.27.0 -RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3 +RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3 vim 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} diff --git a/README.md b/README.md index 56b458595..bf54462bd 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ If you have a new service / build pack you would like to add, raise an idea [her ## How to install -For more details goto the [docs](https://docs.coollabs.io/coolify/installation). +For more details goto the [docs](https://docs.coollabs.io/coolify-v3/installation). Installation is automated with the following command: @@ -79,9 +79,9 @@ Deploy your resource to: ### Services - [Appwrite](https://appwrite.io) -- [WordPress](https://docs.coollabs.io/coolify/services/wordpress) +- [WordPress](https://docs.coollabs.io/coolify-v3/services/wordpress) - [Ghost](https://ghost.org) -- [Plausible Analytics](https://docs.coollabs.io/coolify/services/plausible-analytics) +- [Plausible Analytics](https://docs.coollabs.io/coolify-v3/services/plausible-analytics) - [NocoDB](https://nocodb.com) - [VSCode Server](https://github.com/cdr/code-server) - [MinIO](https://min.io) diff --git a/apps/api/package.json b/apps/api/package.json index 86f78ab39..32b303b4f 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -26,8 +26,6 @@ "@iarna/toml": "2.2.5", "@ladjs/graceful": "3.2.1", "@prisma/client": "4.8.1", - "@sentry/node": "7.30.0", - "@sentry/tracing": "7.30.0", "axe": "11.2.1", "bcryptjs": "2.4.3", "bree": "9.1.3", diff --git a/apps/api/prisma/seed.js b/apps/api/prisma/seed.js index 3d4b16a28..06affc8a7 100644 --- a/apps/api/prisma/seed.js +++ b/apps/api/prisma/seed.js @@ -12,7 +12,7 @@ async function main() { await prisma.setting.create({ data: { id: '0', - arch: process.arch, + arch: process.arch } }); } else { @@ -81,16 +81,246 @@ async function main() { }); } // Set new preview secrets - const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } }) + const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } }); if (secrets.length > 0) { for (const secret of secrets) { - const previewSecrets = await prisma.secret.findMany({ where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true } }) + const previewSecrets = await prisma.secret.findMany({ + where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true } + }); if (previewSecrets.length === 0) { - await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } }) + await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } }); } } } } +async function reEncryptSecrets() { + const { execaCommand } = await import('execa'); + const date = new Date().getTime(); + await execaCommand('env | grep COOLIFY > .env', { shell: true }); + const secretOld = process.env['COOLIFY_SECRET_KEY']; + let secretNew = process.env['COOLIFY_SECRET_KEY_BETTER']; + if (!secretNew) { + console.log('No COOLIFY_SECRET_KEY_BETTER found... Generating new one...'); + const { stdout: newKey } = await execaCommand( + 'openssl rand -base64 1024 | sha256sum | base64 | head -c 32', + { shell: true } + ); + secretNew = newKey; + } + if (secretOld !== secretNew) { + console.log( + 'Secrets (COOLIFY_SECRET_KEY & COOLIFY_SECRET_KEY_BETTER) are different, so re-encrypting everything...' + ); + await execaCommand(`sed -i '/COOLIFY_SECRET_KEY=/d' .env`, { shell: true }); + await execaCommand(`sed -i '/COOLIFY_SECRET_KEY_BETTER=/d' .env`, { shell: true }); + await execaCommand(`echo "COOLIFY_SECRET_KEY=${secretNew}" >> .env`, { shell: true }); + await execaCommand('echo "COOLIFY_SECRET_KEY_BETTER=' + secretNew + '" >> .env ', { + shell: true + }); + await execaCommand(`echo "COOLIFY_SECRET_KEY_OLD_${date}=${secretOld}" >> .env`, { + shell: true + }); + console.log(`Backup database to /app/db/prod.db_${date}.`); + await execaCommand(`cp /app/db/prod.db /app/db/prod.db_${date}`, { shell: true }); + const transactions = []; + const secrets = await prisma.secret.findMany(); + if (secrets.length > 0) { + for (const secret of secrets) { + const value = decrypt(secret.value, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.secret.update({ + where: { id: secret.id }, + data: { value: newValue } + }) + ); + } + } + const serviceSecrets = await prisma.serviceSecret.findMany(); + if (serviceSecrets.length > 0) { + for (const secret of serviceSecrets) { + const value = decrypt(secret.value, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.serviceSecret.update({ + where: { id: secret.id }, + data: { value: newValue } + }) + ); + } + } + const gitlabApps = await prisma.gitlabApp.findMany(); + if (gitlabApps.length > 0) { + for (const gitlabApp of gitlabApps) { + const value = decrypt(gitlabApp.privateSshKey, secretOld); + const newValue = encrypt(value, secretNew); + const appSecret = decrypt(gitlabApp.appSecret, secretOld); + const newAppSecret = encrypt(appSecret, secretNew); + transactions.push( + prisma.gitlabApp.update({ + where: { id: gitlabApp.id }, + data: { privateSshKey: newValue, appSecret: newAppSecret } + }) + ); + } + } + const githubApps = await prisma.githubApp.findMany(); + if (githubApps.length > 0) { + for (const githubApp of githubApps) { + const clientSecret = decrypt(githubApp.clientSecret, secretOld); + const newClientSecret = encrypt(clientSecret, secretNew); + const webhookSecret = decrypt(githubApp.webhookSecret, secretOld); + const newWebhookSecret = encrypt(webhookSecret, secretNew); + const privateKey = decrypt(githubApp.privateKey, secretOld); + const newPrivateKey = encrypt(privateKey, secretNew); + + transactions.push( + prisma.githubApp.update({ + where: { id: githubApp.id }, + data: { + clientSecret: newClientSecret, + webhookSecret: newWebhookSecret, + privateKey: newPrivateKey + } + }) + ); + } + } + const databases = await prisma.database.findMany(); + if (databases.length > 0) { + for (const database of databases) { + const dbUserPassword = decrypt(database.dbUserPassword, secretOld); + const newDbUserPassword = encrypt(dbUserPassword, secretNew); + const rootUserPassword = decrypt(database.rootUserPassword, secretOld); + const newRootUserPassword = encrypt(rootUserPassword, secretNew); + transactions.push( + prisma.database.update({ + where: { id: database.id }, + data: { + dbUserPassword: newDbUserPassword, + rootUserPassword: newRootUserPassword + } + }) + ); + } + } + const databaseSecrets = await prisma.databaseSecret.findMany(); + if (databaseSecrets.length > 0) { + for (const databaseSecret of databaseSecrets) { + const value = decrypt(databaseSecret.value, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.databaseSecret.update({ + where: { id: databaseSecret.id }, + data: { value: newValue } + }) + ); + } + } + const wordpresses = await prisma.wordpress.findMany(); + if (wordpresses.length > 0) { + for (const wordpress of wordpresses) { + const value = decrypt(wordpress.ftpHostKey, secretOld); + const newValue = encrypt(value, secretNew); + const ftpHostKeyPrivate = decrypt(wordpress.ftpHostKeyPrivate, secretOld); + const newFtpHostKeyPrivate = encrypt(ftpHostKeyPrivate, secretNew); + let newFtpPassword = undefined; + if (wordpress.ftpPassword != null) { + const ftpPassword = decrypt(wordpress.ftpPassword, secretOld); + newFtpPassword = encrypt(ftpPassword, secretNew); + } + + transactions.push( + prisma.wordpress.update({ + where: { id: wordpress.id }, + data: { + ftpHostKey: newValue, + ftpHostKeyPrivate: newFtpHostKeyPrivate, + ftpPassword: newFtpPassword + } + }) + ); + } + } + const sshKeys = await prisma.sshKey.findMany(); + if (sshKeys.length > 0) { + for (const key of sshKeys) { + const value = decrypt(key.privateKey, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.sshKey.update({ + where: { id: key.id }, + data: { + privateKey: newValue + } + }) + ); + } + } + const dockerRegistries = await prisma.dockerRegistry.findMany(); + if (dockerRegistries.length > 0) { + for (const registry of dockerRegistries) { + const value = decrypt(registry.password, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.dockerRegistry.update({ + where: { id: registry.id }, + data: { + password: newValue + } + }) + ); + } + } + const certificates = await prisma.certificate.findMany(); + if (certificates.length > 0) { + for (const certificate of certificates) { + const value = decrypt(certificate.key, secretOld); + const newValue = encrypt(value, secretNew); + transactions.push( + prisma.certificate.update({ + where: { id: certificate.id }, + data: { + key: newValue + } + }) + ); + } + } + await prisma.$transaction(transactions); + } else { + console.log('secrets are the same, so no need to re-encrypt'); + } +} + +const encrypt = (text, secret) => { + if (text && secret) { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(algorithm, secret, iv); + const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]); + return JSON.stringify({ + iv: iv.toString('hex'), + content: encrypted.toString('hex') + }); + } +}; +const decrypt = (hashString, secret) => { + if (hashString && secret) { + try { + const hash = JSON.parse(hashString); + const decipher = crypto.createDecipheriv(algorithm, secret, Buffer.from(hash.iv, 'hex')); + const decrpyted = Buffer.concat([ + decipher.update(Buffer.from(hash.content, 'hex')), + decipher.final() + ]); + return decrpyted.toString(); + } catch (error) { + console.log({ decryptionError: error.message }); + return hashString; + } + } +}; + main() .catch((e) => { console.error(e); @@ -99,15 +329,11 @@ main() .finally(async () => { await prisma.$disconnect(); }); - -const encrypt = (text) => { - if (text) { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); - const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); - return JSON.stringify({ - iv: iv.toString('hex'), - content: encrypted.toString('hex') - }); - } -}; \ No newline at end of file +reEncryptSecrets() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index e0f57be5d..2d57842e9 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -18,7 +18,6 @@ import { isDev, listSettings, prisma, - sentryDSN, startTraefikProxy, startTraefikTCPProxy, version @@ -32,12 +31,12 @@ import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handler import { checkContainer } from './lib/docker'; import { migrateApplicationPersistentStorage, migrateServicesToNewTemplate } from './lib'; import { refreshTags, refreshTemplates } from './routes/api/v1/handlers'; -import * as Sentry from '@sentry/node'; declare module 'fastify' { interface FastifyInstance { config: { COOLIFY_APP_ID: string; COOLIFY_SECRET_KEY: string; + COOLIFY_SECRET_KEY_BETTER: string | null; COOLIFY_DATABASE_URL: string; COOLIFY_IS_ON: string; COOLIFY_WHITE_LABELED: string; @@ -67,6 +66,10 @@ const host = '0.0.0.0'; COOLIFY_SECRET_KEY: { type: 'string' }, + COOLIFY_SECRET_KEY_BETTER: { + type: 'string', + default: null + }, COOLIFY_DATABASE_URL: { type: 'string', default: 'file:../db/dev.db' @@ -276,9 +279,6 @@ async function initServer() { if (settings.doNotTrack === true) { console.log('[000] Telemetry disabled...'); } else { - if (settings.sentryDSN !== sentryDSN) { - await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } }); - } // Initialize Sentry // Sentry.init({ // dsn: sentryDSN, @@ -402,7 +402,9 @@ async function autoUpdater() { if (!isDev) { const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); if (isAutoUpdateEnabled) { - await executeCommand({ command: `docker pull ghcr.io/coollabsio/coolify:${latestVersion}` }); + await executeCommand({ + command: `docker pull ghcr.io/coollabsio/coolify:${latestVersion}` + }); await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` }); await executeCommand({ command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` @@ -651,7 +653,7 @@ async function cleanupStorage() { // } // } catch (error) {} // if (lowDiskSpace) { - // await cleanupDockerStorage(destination.id); + // await cleanupDockerStorage(destination.id); // } } } diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 366cce212..b64b1bbe7 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -8,7 +8,6 @@ import type { Config } from 'unique-names-generator'; import generator from 'generate-password'; import crypto from 'crypto'; import { promises as dns } from 'dns'; -import * as Sentry from '@sentry/node'; import { PrismaClient } from '@prisma/client'; import os from 'os'; import * as SSHConfig from 'ssh-config/src/ssh-config'; @@ -19,12 +18,11 @@ import { saveBuildLog } from './buildPacks/common'; import { scheduler } from './scheduler'; import type { ExecaChildProcess } from 'execa'; -export const version = '3.12.32'; +export const version = '3.12.33'; export const isDev = process.env.NODE_ENV === 'development'; export const proxyPort = process.env.COOLIFY_PROXY_PORT; export const proxySecurePort = process.env.COOLIFY_PROXY_SECURE_PORT; -export const sentryDSN = - 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; + const algorithm = 'aes-256-ctr'; const customConfig: Config = { dictionaries: [adjectives, colors, animals], @@ -172,13 +170,19 @@ export const base64Encode = (text: string): string => { export const base64Decode = (text: string): string => { return Buffer.from(text, 'base64').toString('ascii'); }; +export const getSecretKey = () => { + if (process.env['COOLIFY_SECRET_KEY_BETTER']) { + return process.env['COOLIFY_SECRET_KEY_BETTER']; + } + return process.env['COOLIFY_SECRET_KEY']; +}; export const decrypt = (hashString: string) => { if (hashString) { try { const hash = JSON.parse(hashString); const decipher = crypto.createDecipheriv( algorithm, - process.env['COOLIFY_SECRET_KEY'], + getSecretKey(), Buffer.from(hash.iv, 'hex') ); const decrpyted = Buffer.concat([ @@ -195,7 +199,7 @@ export const decrypt = (hashString: string) => { export const encrypt = (text: string) => { if (text) { const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); + const cipher = crypto.createCipheriv(algorithm, getSecretKey(), iv); const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]); return JSON.stringify({ iv: iv.toString('hex'), @@ -841,7 +845,7 @@ export function generateToken() { { nbf: Math.floor(Date.now() / 1000) - 30 }, - process.env['COOLIFY_SECRET_KEY'] + getSecretKey() ); } export function generatePassword({ @@ -1678,9 +1682,6 @@ export function errorHandler({ if (message.includes('Unique constraint failed')) { message = 'This data is unique and already exists. Please try again with a different value.'; } - if (type === 'normal') { - Sentry.captureException(message); - } throw { status, message }; } export async function generateSshKeyPair(): Promise<{ publicKey: string; privateKey: string }> { diff --git a/apps/api/src/plugins/jwt.ts b/apps/api/src/plugins/jwt.ts index 029aecd94..1a50fca4f 100644 --- a/apps/api/src/plugins/jwt.ts +++ b/apps/api/src/plugins/jwt.ts @@ -1,33 +1,37 @@ -import fp from 'fastify-plugin' -import fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt' +import fp from 'fastify-plugin'; +import fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt'; -declare module "@fastify/jwt" { - interface FastifyJWT { - user: { - userId: string, - teamId: string, - permission: string, - isAdmin: boolean - } - } +declare module '@fastify/jwt' { + interface FastifyJWT { + user: { + userId: string; + teamId: string; + permission: string; + isAdmin: boolean; + }; + } } export default fp(async (fastify, opts) => { - fastify.register(fastifyJwt, { - secret: fastify.config.COOLIFY_SECRET_KEY - }) + let secretKey = fastify.config.COOLIFY_SECRET_KEY_BETTER; + if (!secretKey) { + secretKey = fastify.config.COOLIFY_SECRET_KEY; + } + fastify.register(fastifyJwt, { + secret: secretKey + }); - fastify.decorate("authenticate", async function (request, reply) { - try { - await request.jwtVerify() - } catch (err) { - reply.send(err) - } - }) -}) + fastify.decorate('authenticate', async function (request, reply) { + try { + await request.jwtVerify(); + } catch (err) { + reply.send(err); + } + }); +}); declare module 'fastify' { - export interface FastifyInstance { - authenticate(): Promise - } + export interface FastifyInstance { + authenticate(): Promise; + } } diff --git a/apps/api/src/routes/api/v1/handlers.ts b/apps/api/src/routes/api/v1/handlers.ts index 5cb283137..a9525b74f 100644 --- a/apps/api/src/routes/api/v1/handlers.ts +++ b/apps/api/src/routes/api/v1/handlers.ts @@ -12,7 +12,6 @@ import { prisma, uniqueName, version, - sentryDSN, executeCommand } from '../../../lib/common'; import { scheduler } from '../../../lib/scheduler'; @@ -164,7 +163,7 @@ export async function update(request: FastifyRequest) { await executeCommand({ command: `docker pull ${image}` }); } - await executeCommand({ shell: true, command: `env | grep COOLIFY > .env` }); + await executeCommand({ shell: true, command: `ls .env || env | grep COOLIFY > .env` }); await executeCommand({ command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` }); @@ -452,7 +451,6 @@ export async function getCurrentUser(request: FastifyRequest, fa }); return { settings: await prisma.setting.findUnique({ where: { id: '0' } }), - sentryDSN, pendingInvitations, token, ...request.user diff --git a/apps/api/src/routes/api/v1/settings/handlers.ts b/apps/api/src/routes/api/v1/settings/handlers.ts index 1f0ecb006..929bef1b8 100644 --- a/apps/api/src/routes/api/v1/settings/handlers.ts +++ b/apps/api/src/routes/api/v1/settings/handlers.ts @@ -1,235 +1,312 @@ import { promises as dns } from 'dns'; import { X509Certificate } from 'node:crypto'; -import * as Sentry from '@sentry/node'; import type { FastifyReply, FastifyRequest } from 'fastify'; -import { checkDomainsIsValidInDNS, decrypt, encrypt, errorHandler, executeCommand, getDomain, isDev, isDNSValid, isDomainConfigured, listSettings, prisma, sentryDSN, version } from '../../../../lib/common'; -import { AddDefaultRegistry, CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; - +import { + checkDomainsIsValidInDNS, + decrypt, + encrypt, + errorHandler, + executeCommand, + getDomain, + isDev, + isDNSValid, + isDomainConfigured, + listSettings, + prisma +} from '../../../../lib/common'; +import { + AddDefaultRegistry, + CheckDNS, + CheckDomain, + DeleteDomain, + OnlyIdInBody, + SaveSettings, + SaveSSHKey, + SetDefaultRegistry +} from './types'; export async function listAllSettings(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const settings = await listSettings(); - const sshKeys = await prisma.sshKey.findMany({ where: { team: { id: teamId } } }) - let registries = await prisma.dockerRegistry.findMany({ where: { team: { id: teamId } } }) - registries = registries.map((registry) => { - if (registry.password) { - registry.password = decrypt(registry.password) - } - return registry - }) - const unencryptedKeys = [] - if (sshKeys.length > 0) { - for (const key of sshKeys) { - unencryptedKeys.push({ id: key.id, name: key.name, privateKey: decrypt(key.privateKey), createdAt: key.createdAt }) - } - } - const certificates = await prisma.certificate.findMany({ where: { team: { id: teamId } } }) - let cns = []; - for (const certificate of certificates) { - const x509 = new X509Certificate(certificate.cert); - cns.push({ commonName: x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', ''), id: certificate.id, createdAt: certificate.createdAt }) - } + try { + const teamId = request.user.teamId; + const settings = await listSettings(); + const sshKeys = await prisma.sshKey.findMany({ where: { team: { id: teamId } } }); + let registries = await prisma.dockerRegistry.findMany({ where: { team: { id: teamId } } }); + registries = registries.map((registry) => { + if (registry.password) { + registry.password = decrypt(registry.password); + } + return registry; + }); + const unencryptedKeys = []; + if (sshKeys.length > 0) { + for (const key of sshKeys) { + unencryptedKeys.push({ + id: key.id, + name: key.name, + privateKey: decrypt(key.privateKey), + createdAt: key.createdAt + }); + } + } + const certificates = await prisma.certificate.findMany({ where: { team: { id: teamId } } }); + let cns = []; + for (const certificate of certificates) { + const x509 = new X509Certificate(certificate.cert); + cns.push({ + commonName: x509.subject + .split('\n') + .find((s) => s.startsWith('CN=')) + .replace('CN=', ''), + id: certificate.id, + createdAt: certificate.createdAt + }); + } - return { - settings, - certificates: cns, - sshKeys: unencryptedKeys, - registries - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + return { + settings, + certificates: cns, + sshKeys: unencryptedKeys, + registries + }; + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function saveSettings(request: FastifyRequest, reply: FastifyReply) { - try { - let { - previewSeparator, - numberOfDockerImagesKeptLocally, - doNotTrack, - fqdn, - isAPIDebuggingEnabled, - isRegistrationEnabled, - dualCerts, - minPort, - maxPort, - isAutoUpdateEnabled, - isDNSCheckEnabled, - DNSServers, - proxyDefaultRedirect - } = request.body - const { id, previewSeparator: SetPreviewSeparator } = await listSettings(); - if (numberOfDockerImagesKeptLocally) { - numberOfDockerImagesKeptLocally = Number(numberOfDockerImagesKeptLocally) - } - if (previewSeparator == '') { - previewSeparator = '.' - } - if (SetPreviewSeparator != previewSeparator) { - const applications = await prisma.application.findMany({ where: { previewApplication: { some: { id: { not: undefined } } } }, include: { previewApplication: true } }) - for (const application of applications) { - for (const preview of application.previewApplication) { - const { protocol } = new URL(preview.customDomain) - const { pullmergeRequestId } = preview - const { fqdn } = application - const newPreviewDomain = `${protocol}//${pullmergeRequestId}${previewSeparator}${getDomain(fqdn)}` - await prisma.previewApplication.update({ where: { id: preview.id }, data: { customDomain: newPreviewDomain } }) - } - } - } + try { + let { + previewSeparator, + numberOfDockerImagesKeptLocally, + doNotTrack, + fqdn, + isAPIDebuggingEnabled, + isRegistrationEnabled, + dualCerts, + minPort, + maxPort, + isAutoUpdateEnabled, + isDNSCheckEnabled, + DNSServers, + proxyDefaultRedirect + } = request.body; + const { id, previewSeparator: SetPreviewSeparator } = await listSettings(); + if (numberOfDockerImagesKeptLocally) { + numberOfDockerImagesKeptLocally = Number(numberOfDockerImagesKeptLocally); + } + if (previewSeparator == '') { + previewSeparator = '.'; + } + if (SetPreviewSeparator != previewSeparator) { + const applications = await prisma.application.findMany({ + where: { previewApplication: { some: { id: { not: undefined } } } }, + include: { previewApplication: true } + }); + for (const application of applications) { + for (const preview of application.previewApplication) { + const { protocol } = new URL(preview.customDomain); + const { pullmergeRequestId } = preview; + const { fqdn } = application; + const newPreviewDomain = `${protocol}//${pullmergeRequestId}${previewSeparator}${getDomain( + fqdn + )}`; + await prisma.previewApplication.update({ + where: { id: preview.id }, + data: { customDomain: newPreviewDomain } + }); + } + } + } - await prisma.setting.update({ - where: { id }, - data: { previewSeparator, numberOfDockerImagesKeptLocally, doNotTrack, isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled } - }); - if (fqdn) { - await prisma.setting.update({ where: { id }, data: { fqdn } }); - } - await prisma.setting.update({ where: { id }, data: { proxyDefaultRedirect } }); - if (minPort && maxPort) { - await prisma.setting.update({ where: { id }, data: { minPort, maxPort } }); - } - if (doNotTrack === false) { - // Sentry.init({ - // dsn: sentryDSN, - // environment: isDev ? 'development' : 'production', - // release: version - // }); - // console.log('Sentry initialized') - } - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + await prisma.setting.update({ + where: { id }, + data: { + previewSeparator, + numberOfDockerImagesKeptLocally, + doNotTrack, + isRegistrationEnabled, + dualCerts, + isAutoUpdateEnabled, + isDNSCheckEnabled, + DNSServers, + isAPIDebuggingEnabled + } + }); + if (fqdn) { + await prisma.setting.update({ where: { id }, data: { fqdn } }); + } + await prisma.setting.update({ where: { id }, data: { proxyDefaultRedirect } }); + if (minPort && maxPort) { + await prisma.setting.update({ where: { id }, data: { minPort, maxPort } }); + } + if (doNotTrack === false) { + // Sentry.init({ + // dsn: sentryDSN, + // environment: isDev ? 'development' : 'production', + // release: version + // }); + // console.log('Sentry initialized') + } + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function deleteDomain(request: FastifyRequest, reply: FastifyReply) { - try { - const { fqdn } = request.body - const { DNSServers } = await listSettings(); - if (DNSServers) { - dns.setServers([...DNSServers.split(',')]); - } - let ip; - try { - ip = await dns.resolve(fqdn); - } catch (error) { - // Do not care. - } - await prisma.setting.update({ where: { fqdn }, data: { fqdn: null } }); - return reply.redirect(302, ip ? `http://${ip[0]}:3000/settings` : undefined) - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + try { + const { fqdn } = request.body; + const { DNSServers } = await listSettings(); + if (DNSServers) { + dns.setServers([...DNSServers.split(',')]); + } + let ip; + try { + ip = await dns.resolve(fqdn); + } catch (error) { + // Do not care. + } + await prisma.setting.update({ where: { fqdn }, data: { fqdn: null } }); + return reply.redirect(302, ip ? `http://${ip[0]}:3000/settings` : undefined); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function checkDomain(request: FastifyRequest) { - try { - const { id } = request.params; - let { fqdn, forceSave, dualCerts, isDNSCheckEnabled } = request.body - if (fqdn) fqdn = fqdn.toLowerCase(); - const found = await isDomainConfigured({ id, fqdn }); - if (found) { - throw { message: "Domain already configured" }; - } - if (isDNSCheckEnabled && !forceSave && !isDev) { - const hostname = request.hostname.split(':')[0] - return await checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }); - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + try { + const { id } = request.params; + let { fqdn, forceSave, dualCerts, isDNSCheckEnabled } = request.body; + if (fqdn) fqdn = fqdn.toLowerCase(); + const found = await isDomainConfigured({ id, fqdn }); + if (found) { + throw { message: 'Domain already configured' }; + } + if (isDNSCheckEnabled && !forceSave && !isDev) { + const hostname = request.hostname.split(':')[0]; + return await checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }); + } + return {}; + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function checkDNS(request: FastifyRequest) { - try { - const { domain } = request.params; - await isDNSValid(request.hostname, domain); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + try { + const { domain } = request.params; + await isDNSValid(request.hostname, domain); + return {}; + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function saveSSHKey(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { privateKey, name } = request.body; - const found = await prisma.sshKey.findMany({ where: { name } }) - if (found.length > 0) { - throw { - message: "Name already used. Choose another one please." - } - } - const encryptedSSHKey = encrypt(privateKey) - await prisma.sshKey.create({ data: { name, privateKey: encryptedSSHKey, team: { connect: { id: teamId } } } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + try { + const teamId = request.user.teamId; + const { privateKey, name } = request.body; + const found = await prisma.sshKey.findMany({ where: { name } }); + if (found.length > 0) { + throw { + message: 'Name already used. Choose another one please.' + }; + } + const encryptedSSHKey = encrypt(privateKey); + await prisma.sshKey.create({ + data: { name, privateKey: encryptedSSHKey, team: { connect: { id: teamId } } } + }); + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } export async function deleteSSHKey(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await prisma.sshKey.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + try { + const teamId = request.user.teamId; + const { id } = request.body; + await prisma.sshKey.deleteMany({ where: { id, teamId } }); + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } -export async function deleteCertificates(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await executeCommand({ command: `docker exec coolify-proxy sh -c 'rm -f /etc/traefik/acme/custom/${id}-key.pem /etc/traefik/acme/custom/${id}-cert.pem'`, shell: true }) - await prisma.certificate.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } +export async function deleteCertificates( + request: FastifyRequest, + reply: FastifyReply +) { + try { + const teamId = request.user.teamId; + const { id } = request.body; + await executeCommand({ + command: `docker exec coolify-proxy sh -c 'rm -f /etc/traefik/acme/custom/${id}-key.pem /etc/traefik/acme/custom/${id}-cert.pem'`, + shell: true + }); + await prisma.certificate.deleteMany({ where: { id, teamId } }); + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } -export async function setDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id, username, password } = request.body; +export async function setDockerRegistry( + request: FastifyRequest, + reply: FastifyReply +) { + try { + const teamId = request.user.teamId; + const { id, username, password } = request.body; - let encryptedPassword = '' - if (password) encryptedPassword = encrypt(password) + let encryptedPassword = ''; + if (password) encryptedPassword = encrypt(password); - if (teamId === '0') { - await prisma.dockerRegistry.update({ where: { id }, data: { username, password: encryptedPassword } }) - } else { - await prisma.dockerRegistry.updateMany({ where: { id, teamId }, data: { username, password: encryptedPassword } }) - } - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + if (teamId === '0') { + await prisma.dockerRegistry.update({ + where: { id }, + data: { username, password: encryptedPassword } + }); + } else { + await prisma.dockerRegistry.updateMany({ + where: { id, teamId }, + data: { username, password: encryptedPassword } + }); + } + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } -export async function addDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { name, url, username, password } = request.body; +export async function addDockerRegistry( + request: FastifyRequest, + reply: FastifyReply +) { + try { + const teamId = request.user.teamId; + const { name, url, username, password } = request.body; - let encryptedPassword = '' - if (password) encryptedPassword = encrypt(password) - await prisma.dockerRegistry.create({ data: { name, url, username, password: encryptedPassword, team: { connect: { id: teamId } } } }) + let encryptedPassword = ''; + if (password) encryptedPassword = encrypt(password); + await prisma.dockerRegistry.create({ + data: { name, url, username, password: encryptedPassword, team: { connect: { id: teamId } } } + }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } +} +export async function deleteDockerRegistry( + request: FastifyRequest, + reply: FastifyReply +) { + try { + const teamId = request.user.teamId; + const { id } = request.body; + await prisma.application.updateMany({ + where: { dockerRegistryId: id }, + data: { dockerRegistryId: null } + }); + await prisma.dockerRegistry.deleteMany({ where: { id, teamId } }); + return reply.code(201).send(); + } catch ({ status, message }) { + return errorHandler({ status, message }); + } } -export async function deleteDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await prisma.application.updateMany({ where: { dockerRegistryId: id }, data: { dockerRegistryId: null } }) - await prisma.dockerRegistry.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} \ No newline at end of file diff --git a/apps/ui/package.json b/apps/ui/package.json index d40dba2a0..1cadeffd8 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -42,8 +42,6 @@ }, "type": "module", "dependencies": { - "@sentry/svelte": "7.21.1", - "@sentry/tracing": "7.21.1", "@sveltejs/adapter-static": "1.0.0-next.48", "@tailwindcss/typography": "0.5.8", "cuid": "2.1.8", diff --git a/apps/ui/src/hooks.ts b/apps/ui/src/hooks.ts index df3284d5a..6723af14f 100644 --- a/apps/ui/src/hooks.ts +++ b/apps/ui/src/hooks.ts @@ -1,13 +1,10 @@ -import * as Sentry from '@sentry/svelte'; export async function handle({ event, resolve }) { - const response = await resolve(event, { ssr: false }); - return response; + const response = await resolve(event, { ssr: false }); + return response; } export const handleError = ({ error, event }) => { - Sentry.captureException(error, { event }); - - return { - message: 'Whoops!', - code: error?.code ?? 'UNKNOWN' - }; -}; \ No newline at end of file + return { + message: 'Whoops!', + code: error?.code ?? 'UNKNOWN' + }; +}; diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index 4e68957b2..1c432fd5a 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -65,7 +65,6 @@