diff --git a/package.json b/package.json index 4f3fc8ef4..4e84cb3e1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.0.11", + "version": "2.0.12", "license": "AGPL-3.0", "scripts": { "dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0", @@ -30,7 +30,7 @@ "@sveltejs/kit": "1.0.0-next.259", "@types/bcrypt": "5.0.0", "@types/js-cookie": "3.0.1", - "@types/node": "17.0.16", + "@types/node": "17.0.18", "@types/node-forge": "1.0.0", "@typescript-eslint/eslint-plugin": "4.31.1", "@typescript-eslint/parser": "4.31.1", @@ -41,16 +41,16 @@ "eslint-config-prettier": "8.3.0", "eslint-plugin-svelte3": "3.2.1", "husky": "7.0.4", - "lint-staged": "12.3.3", + "lint-staged": "12.3.4", "postcss": "8.4.6", "prettier": "2.5.1", "prettier-plugin-svelte": "2.6.0", "prettier-plugin-tailwindcss": "0.1.7", - "prisma": "3.9.1", + "prisma": "3.9.2", "svelte": "3.46.4", "svelte-check": "2.4.3", "svelte-preprocess": "4.10.3", - "tailwindcss": "3.0.19", + "tailwindcss": "3.0.22", "ts-node": "10.5.0", "tslib": "2.3.1", "typescript": "4.5.5" @@ -58,10 +58,10 @@ "type": "module", "dependencies": { "@iarna/toml": "2.2.5", - "@prisma/client": "3.9.1", - "@sentry/node": "6.17.6", + "@prisma/client": "3.9.2", + "@sentry/node": "6.17.8", "bcrypt": "5.0.1", - "bullmq": "1.69.0", + "bullmq": "1.72.0", "compare-versions": "4.1.3", "cookie": "0.4.2", "cuid": "2.1.8", @@ -75,7 +75,7 @@ "js-yaml": "4.1.0", "jsonwebtoken": "8.5.1", "node-forge": "1.2.1", - "svelte-kit-cookie-session": "2.0.3", + "svelte-kit-cookie-session": "2.0.5", "unique-names-generator": "4.6.0" }, "prisma": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76d4ab997..58ddfd8c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,21 +2,21 @@ lockfileVersion: 5.3 specifiers: '@iarna/toml': 2.2.5 - '@prisma/client': 3.9.1 - '@sentry/node': 6.17.6 + '@prisma/client': 3.9.2 + '@sentry/node': 6.17.8 '@sveltejs/adapter-node': 1.0.0-next.67 '@sveltejs/adapter-static': 1.0.0-next.27 '@sveltejs/kit': 1.0.0-next.259 '@types/bcrypt': 5.0.0 '@types/js-cookie': 3.0.1 - '@types/node': 17.0.16 + '@types/node': 17.0.18 '@types/node-forge': 1.0.0 '@typescript-eslint/eslint-plugin': 4.31.1 '@typescript-eslint/parser': 4.31.1 '@zerodevx/svelte-toast': 0.6.3 autoprefixer: 10.4.2 bcrypt: 5.0.1 - bullmq: 1.69.0 + bullmq: 1.72.0 compare-versions: 4.1.3 cookie: 0.4.2 cross-var: 1.1.0 @@ -34,18 +34,18 @@ specifiers: js-cookie: 3.0.1 js-yaml: 4.1.0 jsonwebtoken: 8.5.1 - lint-staged: 12.3.3 + lint-staged: 12.3.4 node-forge: 1.2.1 postcss: 8.4.6 prettier: 2.5.1 prettier-plugin-svelte: 2.6.0 prettier-plugin-tailwindcss: 0.1.7 - prisma: 3.9.1 + prisma: 3.9.2 svelte: 3.46.4 svelte-check: 2.4.3 - svelte-kit-cookie-session: 2.0.3 + svelte-kit-cookie-session: 2.0.5 svelte-preprocess: 4.10.3 - tailwindcss: 3.0.19 + tailwindcss: 3.0.22 ts-node: 10.5.0 tslib: 2.3.1 typescript: 4.5.5 @@ -53,10 +53,10 @@ specifiers: dependencies: '@iarna/toml': 2.2.5 - '@prisma/client': 3.9.1_prisma@3.9.1 - '@sentry/node': 6.17.6 + '@prisma/client': 3.9.2_prisma@3.9.2 + '@sentry/node': 6.17.8 bcrypt: 5.0.1 - bullmq: 1.69.0 + bullmq: 1.72.0 compare-versions: 4.1.3 cookie: 0.4.2 cuid: 2.1.8 @@ -70,7 +70,7 @@ dependencies: js-yaml: 4.1.0 jsonwebtoken: 8.5.1 node-forge: 1.2.1 - svelte-kit-cookie-session: 2.0.3 + svelte-kit-cookie-session: 2.0.5 unique-names-generator: 4.6.0 devDependencies: @@ -79,7 +79,7 @@ devDependencies: '@sveltejs/kit': 1.0.0-next.259_svelte@3.46.4 '@types/bcrypt': 5.0.0 '@types/js-cookie': 3.0.1 - '@types/node': 17.0.16 + '@types/node': 17.0.18 '@types/node-forge': 1.0.0 '@typescript-eslint/eslint-plugin': 4.31.1_5d7752337e5ea49772097d8af1823bf9 '@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.5.5 @@ -90,17 +90,17 @@ devDependencies: eslint-config-prettier: 8.3.0_eslint@7.32.0 eslint-plugin-svelte3: 3.2.1_eslint@7.32.0+svelte@3.46.4 husky: 7.0.4 - lint-staged: 12.3.3 + lint-staged: 12.3.4 postcss: 8.4.6 prettier: 2.5.1 prettier-plugin-svelte: 2.6.0_prettier@2.5.1+svelte@3.46.4 prettier-plugin-tailwindcss: 0.1.7_prettier@2.5.1 - prisma: 3.9.1 + prisma: 3.9.2 svelte: 3.46.4 svelte-check: 2.4.3_postcss@8.4.6+svelte@3.46.4 svelte-preprocess: 4.10.3_88b359da5cac6d8f6ee1bbb7080a3fa9 - tailwindcss: 3.0.19_27d966e3a2f4b84fbc8a2f9653dbb362 - ts-node: 10.5.0_99ae9436e134a034c8d45fdd98ebbf22 + tailwindcss: 3.0.22_c940fbabf228b85b1c73d314b43e31f1 + ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2 tslib: 2.3.1 typescript: 4.5.5 @@ -250,10 +250,10 @@ packages: fastq: 1.13.0 dev: true - /@prisma/client/3.9.1_prisma@3.9.1: + /@prisma/client/3.9.2_prisma@3.9.2: resolution: { - integrity: sha512-aLwfXKLvL+loQ0IuPPCXkcq8cXBg1IeoHHa5lqQu3dJHdj45wnislA/Ny4UxRQjD5FXqrfAb8sWtF+jhdmjFTg== + integrity: sha512-VlEIYVMyfFZHbVBOlunPl47gmP/Z0zzPjPj8I7uKEIaABqrUy50ru3XS0aZd8GFvevVwt7p91xxkUjNjrWhKAQ== } engines: { node: '>=12.6' } requiresBuild: true @@ -264,7 +264,7 @@ packages: optional: true dependencies: '@prisma/engines-version': 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009 - prisma: 3.9.1 + prisma: 3.9.2 dev: false /@prisma/engines-version/3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009: @@ -293,56 +293,56 @@ packages: picomatch: 2.3.0 dev: true - /@sentry/core/6.17.6: + /@sentry/core/6.17.8: resolution: { - integrity: sha512-wSNsQSqsW8vQ2HEvUEXYOJnzTyVDSWbyH4RHrWV1pQM8zqGx/qfz0sKFM5XFnE9ZeaXKL8LXV3v5i73v+z8lew== + integrity: sha512-4WTjgQom75Rvgn6XYy6e7vMIbWlj8utau1wWvr7kjqFKuuuuycRvPgVzAdVr4B3WDHHCInAZpUchsOLs2qwIEA== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.6 - '@sentry/minimal': 6.17.6 - '@sentry/types': 6.17.6 - '@sentry/utils': 6.17.6 + '@sentry/hub': 6.17.8 + '@sentry/minimal': 6.17.8 + '@sentry/types': 6.17.8 + '@sentry/utils': 6.17.8 tslib: 1.14.1 dev: false - /@sentry/hub/6.17.6: + /@sentry/hub/6.17.8: resolution: { - integrity: sha512-Ps9nk+DoFia8jhZ1lucdRE0vDx8hqXOsKXJE8a3hK/Ndki0J9jedYqBeLqSgiFG4qRjXpNFcD6TEM6tnQrv5lw== + integrity: sha512-GW0XYpkoQu/kSJaTLfsF4extHDOBPNRnT0qKr/YO20Z5wGxYp8LsdnAuU3njcFHcAV2F/QDTj2BPq1U385/4+A== } engines: { node: '>=6' } dependencies: - '@sentry/types': 6.17.6 - '@sentry/utils': 6.17.6 + '@sentry/types': 6.17.8 + '@sentry/utils': 6.17.8 tslib: 1.14.1 dev: false - /@sentry/minimal/6.17.6: + /@sentry/minimal/6.17.8: resolution: { - integrity: sha512-PLGf8WlhtdHuY6ofwYR3nyClr/TYHHAW6i0r62OZCOXTqnFPJorZpAz3VCCP2jMJmbgVbo03wN+u/xAA/zwObA== + integrity: sha512-VJXFZBO/O8SViK0fdzodxpNr+pbpgczNgLpz/MNuSooV6EBesgCMVjXtxDUp1Ie1odc0GUprN/ZMLYBmYdIrKQ== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.6 - '@sentry/types': 6.17.6 + '@sentry/hub': 6.17.8 + '@sentry/types': 6.17.8 tslib: 1.14.1 dev: false - /@sentry/node/6.17.6: + /@sentry/node/6.17.8: resolution: { - integrity: sha512-T1s0yPbGvYpoh9pJgLvpy7s+jVwCyf0ieEoN9rSbnPwbi2vm6MfoV5wtGrE0cBHTPgnyOMv+zq4Q3ww6dfr7Pw== + integrity: sha512-b3zg1XjKtxp7o821ENORO1CCzMM4QzKP01rzztMwyMcj28dmUq36QXoQAnwdKn7jEYkJdLnMeniIBR6U6NUJrQ== } engines: { node: '>=6' } dependencies: - '@sentry/core': 6.17.6 - '@sentry/hub': 6.17.6 - '@sentry/tracing': 6.17.6 - '@sentry/types': 6.17.6 - '@sentry/utils': 6.17.6 + '@sentry/core': 6.17.8 + '@sentry/hub': 6.17.8 + '@sentry/tracing': 6.17.8 + '@sentry/types': 6.17.8 + '@sentry/utils': 6.17.8 cookie: 0.4.2 https-proxy-agent: 5.0.0 lru_map: 0.3.3 @@ -351,36 +351,36 @@ packages: - supports-color dev: false - /@sentry/tracing/6.17.6: + /@sentry/tracing/6.17.8: resolution: { - integrity: sha512-+h5ov+zEm5WH9+vmFfdT4EIqBOW7Tggzh0BDz8QRStRc2JbvEiSZDs+HlsycBwWMQi/ucJs93FPtNnWjW+xvBw== + integrity: sha512-WJ3W8O6iPI3w7MrzTnYcw3s5PGBNFqT4b9oBCl5Ndjexs8DsGlQOxjrsipo36z6TpnRHpAE4FEbOETb2R8JRJQ== } engines: { node: '>=6' } dependencies: - '@sentry/hub': 6.17.6 - '@sentry/minimal': 6.17.6 - '@sentry/types': 6.17.6 - '@sentry/utils': 6.17.6 + '@sentry/hub': 6.17.8 + '@sentry/minimal': 6.17.8 + '@sentry/types': 6.17.8 + '@sentry/utils': 6.17.8 tslib: 1.14.1 dev: false - /@sentry/types/6.17.6: + /@sentry/types/6.17.8: resolution: { - integrity: sha512-peGM873lDJtHd/jwW9Egr/hhxLuF0bcPIf2kMZlvEvW/G5GCbuaCR4ArQJlh7vQyma+NLn/XdojpJkC0TomKrw== + integrity: sha512-0i0f+dpvV62Pm5QMVBHNfEsTGIXoXRGQbeN2LGL4XbhzrzUmIrBPzrnZHv9c/JYtSJnI6A0B9OG7Bdlh3aku+Q== } engines: { node: '>=6' } dev: false - /@sentry/utils/6.17.6: + /@sentry/utils/6.17.8: resolution: { - integrity: sha512-RI797N8Ax5yuKUftVX6dc0XmXqo5CN7XqJYPFzYC8udutQ4L8ZYadtUcqNsdz1ZQxl+rp0XK9Q6wjoWmsI2RXA== + integrity: sha512-cAOM53A5FHv95hpDuXKJU8rI4B1XdZ6qe3Yo+/nDS9QDpOgzvyjcItgXPvKW1wUjdHCcnwu7VBfBxB7teYOW9g== } engines: { node: '>=6' } dependencies: - '@sentry/types': 6.17.6 + '@sentry/types': 6.17.8 tslib: 1.14.1 dev: false @@ -502,7 +502,7 @@ packages: integrity: sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw== } dependencies: - '@types/node': 17.0.16 + '@types/node': 17.0.18 dev: true /@types/cacheable-request/6.0.2: @@ -513,7 +513,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.3 - '@types/node': 17.0.16 + '@types/node': 17.0.18 '@types/responselike': 1.0.0 dev: false @@ -544,7 +544,7 @@ packages: integrity: sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== } dependencies: - '@types/node': 17.0.16 + '@types/node': 17.0.18 dev: false /@types/node-forge/1.0.0: @@ -553,13 +553,13 @@ packages: integrity: sha512-h0bgwPKq5u99T9Gor4qtV1lCZ41xNkai0pie1n/a2mh2/4+jENWOlo7AJ4YKxTZAnSZ8FRurUpdIN7ohaPPuHA== } dependencies: - '@types/node': 17.0.16 + '@types/node': 17.0.18 dev: true - /@types/node/17.0.16: + /@types/node/17.0.18: resolution: { - integrity: sha512-ydLaGVfQOQ6hI1xK2A5nVh8bl0OGoIfYMxPWHqqYe9bTkWCfqiVvZoh2I/QF2sNSkZzZyROBoTefIEI+PB6iIA== + integrity: sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== } /@types/parse-json/4.0.0: @@ -582,7 +582,7 @@ packages: integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== } dependencies: - '@types/node': 17.0.16 + '@types/node': 17.0.18 dev: false /@types/sass/1.16.1: @@ -591,7 +591,7 @@ packages: integrity: sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ== } dependencies: - '@types/node': 17.0.16 + '@types/node': 17.0.18 dev: true /@typescript-eslint/eslint-plugin/4.31.1_5d7752337e5ea49772097d8af1823bf9: @@ -1746,10 +1746,10 @@ packages: ieee754: 1.2.1 dev: false - /bullmq/1.69.0: + /bullmq/1.72.0: resolution: { - integrity: sha512-1aIO7bN0HQeADWoXa+I72GgofvoBFRs/kcoveB3KN8ytKv7QJbGhtks0pYNhHn/P9H3OWHWDccpNEfnv3VGfcw== + integrity: sha512-Q0pk6GphHyYsacpjZZFhjp/+TY+2g2FDsJS3qwIyskQL4j7vZaa1iYX3gKDEBn4C5eZMP1EOl9GWkm2bhdB0Wg== } dependencies: cron-parser: 2.18.0 @@ -3718,10 +3718,10 @@ packages: resolution: { integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= } dev: true - /lint-staged/12.3.3: + /lint-staged/12.3.4: resolution: { - integrity: sha512-OqcLsqcPOqzvsfkxjeBpZylgJ3SRG1RYqc9LxC6tkt6tNsq1bNVkAixBwX09f6CobcHswzqVOCBpFR1Fck0+ag== + integrity: sha512-yv/iK4WwZ7/v0GtVkNb3R82pdL9M+ScpIbJLJNyCXkJ1FGaXvRCOg/SeL59SZtPpqZhE7BD6kPKFLIDUhDx2/w== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } hasBin: true @@ -4397,7 +4397,7 @@ packages: dependencies: import-cwd: 3.0.0 lilconfig: 2.0.4 - ts-node: 10.5.0_99ae9436e134a034c8d45fdd98ebbf22 + ts-node: 10.5.0_f3bd4037939c2ed2942ba074291f8ef2 yaml: 1.10.2 dev: true @@ -4486,10 +4486,10 @@ packages: hasBin: true dev: true - /prisma/3.9.1: + /prisma/3.9.2: resolution: { - integrity: sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw== + integrity: sha512-i9eK6cexV74OgeWaH3+e6S07kvC9jEZTl6BqtBH398nlCU0tck7mE9dicY6YQd+euvMjjCtY89q4NgmaPnUsSg== } engines: { node: '>=12.6' } hasBin: true @@ -5203,10 +5203,10 @@ packages: svelte: 3.46.4 dev: true - /svelte-kit-cookie-session/2.0.3: + /svelte-kit-cookie-session/2.0.5: resolution: { - integrity: sha512-jOBUpvrkt/fI5zaqWsWHDDIGnfuPQt3/PC1FDJpEV/E/hA8DvGO52esFny1HvUAP1tkVZ5FU3k6Yd3HyQH5oUQ== + integrity: sha512-IX1IXtn42UTz/isem1LqH0SAZdCx6Z6Iu2V4Q83V2EScFbXZWfeFY08Azl8ZrPKdIDhSNHBLAAumRjA6TBxCvQ== } dev: false @@ -5288,16 +5288,15 @@ packages: strip-ansi: 6.0.1 dev: true - /tailwindcss/3.0.19_27d966e3a2f4b84fbc8a2f9653dbb362: + /tailwindcss/3.0.22_c940fbabf228b85b1c73d314b43e31f1: resolution: { - integrity: sha512-rjsdfz/qZya5xQ0OVynEMETgWq1CacmftgMYeXXh6bRM5vxsNwRSbMJsCCIjq/w67om9VP/AFMolOwiE+5VKig== + integrity: sha512-F8lt74RlNZirnkaSk310+vGQta7c0/hgx7/bqxruM4wS9lp8oqV93lzavajC3VT0Lp4UUtUVIt8ifKcmGzkr0A== } engines: { node: '>=12.13.0' } hasBin: true peerDependencies: autoprefixer: ^10.0.2 - postcss: ^8.0.9 dependencies: arg: 5.0.1 autoprefixer: 10.4.2_postcss@8.4.6 @@ -5408,7 +5407,7 @@ packages: engines: { node: '>=0.10.0' } dev: true - /ts-node/10.5.0_99ae9436e134a034c8d45fdd98ebbf22: + /ts-node/10.5.0_f3bd4037939c2ed2942ba074291f8ef2: resolution: { integrity: sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw== @@ -5430,7 +5429,7 @@ packages: '@tsconfig/node12': 1.0.9 '@tsconfig/node14': 1.0.1 '@tsconfig/node16': 1.0.2 - '@types/node': 17.0.16 + '@types/node': 17.0.18 acorn: 8.5.0 acorn-walk: 8.2.0 arg: 4.1.3 diff --git a/src/lib/buildPacks/common.ts b/src/lib/buildPacks/common.ts index a156864df..67be90ee5 100644 --- a/src/lib/buildPacks/common.ts +++ b/src/lib/buildPacks/common.ts @@ -74,26 +74,12 @@ export async function makeLabelForStandaloneDatabase({ id, image, volume }) { ]; } -export async function makeLabelForPlausibleAnalytics({ id, images, volume }) { - const service = await db.prisma.service.findFirst({ - where: { id }, - include: { plausibleAnalytics: true } - }); - delete service.destinationDockerId; - delete service.createdAt; - delete service.updatedAt; +export function makeLabelForServices(type) { return [ 'coolify.managed=true', `coolify.version=${version}`, - `coolify.type=service-plausibleanalytics`, - `coolify.configuration=${base64Encode( - JSON.stringify({ - version, - images, - volume, - ...service - }) - )}` + `coolify.type=service`, + `coolify.service.type=${type}` ]; } diff --git a/src/lib/database/checks.ts b/src/lib/database/checks.ts index 38ddc96b9..7cc066605 100644 --- a/src/lib/database/checks.ts +++ b/src/lib/database/checks.ts @@ -22,15 +22,15 @@ export async function isSecretExists({ id, name }) { export async function isDomainConfigured({ id, fqdn }) { const domain = getDomain(fqdn); const foundApp = await prisma.application.findFirst({ - where: { fqdn: { endsWith: domain }, id: { not: id } }, + where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, select: { fqdn: true } }); const foundService = await prisma.service.findFirst({ - where: { fqdn: { endsWith: domain }, id: { not: id } }, + where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, select: { fqdn: true } }); const coolifyFqdn = await prisma.setting.findFirst({ - where: { fqdn: { endsWith: domain }, id: { not: id } }, + where: { fqdn: { endsWith: `//${domain}` }, id: { not: id } }, select: { fqdn: true } }); if (foundApp || foundService || coolifyFqdn) return true; diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index cbbc55624..290ecd2b5 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -114,27 +114,55 @@ export const supportedServiceTypesAndVersions = [ name: 'plausibleanalytics', fancyName: 'Plausible Analytics', baseImage: 'plausible/analytics', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 8000 + } + }, + { + name: 'nocodb', + fancyName: 'NocoDB', + baseImage: 'nocodb/nocodb', + versions: ['latest'], + ports: { + main: 8080 + } + }, + { + name: 'minio', + fancyName: 'MinIO', + baseImage: 'minio/minio', + versions: ['latest'], + ports: { + main: 9001 + } }, - { name: 'nocodb', fancyName: 'NocoDB', baseImage: 'nocodb/nocodb', versions: ['latest'] }, - { name: 'minio', fancyName: 'MinIO', baseImage: 'minio/minio', versions: ['latest'] }, { name: 'vscodeserver', fancyName: 'VSCode Server', baseImage: 'codercom/code-server', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 8080 + } }, { name: 'wordpress', fancyName: 'Wordpress', baseImage: 'wordpress', - versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'] + versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'], + ports: { + main: 80 + } }, { name: 'vaultwarden', fancyName: 'Vaultwarden', baseImage: 'vaultwarden/server', - versions: ['latest'] + versions: ['latest'], + ports: { + main: 80 + } } ]; diff --git a/src/lib/database/destinations.ts b/src/lib/database/destinations.ts index 884600bfe..b566becb4 100644 --- a/src/lib/database/destinations.ts +++ b/src/lib/database/destinations.ts @@ -38,7 +38,7 @@ export async function configureDestinationForDatabase({ id, destinationId }) { if (type && version) { const baseImage = getDatabaseImage(type); asyncExecShell( - `DOCKER_HOST=${host} docker pull ${baseImage}:${version} && echo "FROM ${baseImage}:${version}" | docker build --label coolify.managed="true" -t "${baseImage}:${version}" -` + `DOCKER_HOST=${host} docker pull ${baseImage}:${version} && echo "FROM ${baseImage}:${version}" | docker build --label coolify.image="true" -t "${baseImage}:${version}" -` ); } } diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 48b492d63..0eaee0109 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -71,7 +71,7 @@ export async function forceSSLOffApplication({ domain }) { if (!dev) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const transactionId = await getNextTransactionId(); + let transactionId; try { const rules: any = await haproxy .get(`v2/services/haproxy/configuration/http_request_rules`, { @@ -84,6 +84,8 @@ export async function forceSSLOffApplication({ domain }) { if (rules.data.length > 0) { const rule = rules.data.find((rule) => rule.cond_test.includes(`-i ${domain}`)); if (rule) { + transactionId = await getNextTransactionId(); + await haproxy .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { searchParams: { @@ -98,7 +100,7 @@ export async function forceSSLOffApplication({ domain }) { } catch (error) { console.log(error); } finally { - await completeTransaction(transactionId); + if (transactionId) await completeTransaction(transactionId); } } else { console.log(`[DEBUG] Removing ssl for ${domain}`); @@ -108,8 +110,7 @@ export async function forceSSLOnApplication({ domain }) { if (!dev) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const transactionId = await getNextTransactionId(); - + let transactionId; try { const rules: any = await haproxy .get(`v2/services/haproxy/configuration/http_request_rules`, { @@ -127,6 +128,8 @@ export async function forceSSLOnApplication({ domain }) { if (rule) return; nextRule = rules.data[rules.data.length - 1].index + 1; } + transactionId = await getNextTransactionId(); + await haproxy .post(`v2/services/haproxy/configuration/http_request_rules`, { searchParams: { @@ -149,7 +152,7 @@ export async function forceSSLOnApplication({ domain }) { console.log(error); throw error; } finally { - await completeTransaction(transactionId); + if (transactionId) await completeTransaction(transactionId); } } else { console.log(`[DEBUG] Adding ssl for ${domain}`); @@ -159,9 +162,10 @@ export async function forceSSLOnApplication({ domain }) { export async function deleteProxy({ id }) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const transactionId = await getNextTransactionId(); + let transactionId; try { await haproxy.get(`v2/services/haproxy/configuration/backends/${id}`).json(); + transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/backends/${id}`, { searchParams: { @@ -180,7 +184,7 @@ export async function deleteProxy({ id }) { } catch (error) { console.log(error.response.body); } finally { - await completeTransaction(transactionId); + if (transactionId) await completeTransaction(transactionId); } } @@ -212,7 +216,7 @@ export async function configureProxyForApplication({ domain, imageId, applicatio if (backendAvailable.data.forwardfor.enabled === 'enabled') { if (backendAvailable.data.name === domain) { if (server.data.check === 'enabled') { - if (server.data.address === applicationId) { + if (server.data.address === imageId) { if (server.data.port === port) { serverConfigured = true; } @@ -274,8 +278,8 @@ export async function configureCoolifyProxyOff(fqdn) { await checkHAProxy(haproxy); try { - const transactionId = await getNextTransactionId(); await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); + const transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/backends/${domain}`, { searchParams: { @@ -505,52 +509,60 @@ export async function configureNetworkCoolifyProxy(engine) { export async function configureSimpleServiceProxyOn({ id, domain, port }) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); + let serverConfigured = false; + let backendAvailable: any = null; + try { - await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { + backendAvailable = await haproxy + .get(`v2/services/haproxy/configuration/backends/${domain}`) + .json(); + const server: any = await haproxy + .get(`v2/services/haproxy/configuration/servers/${id}`, { searchParams: { - transaction_id: transactionId + backend: domain } }) .json(); - await completeTransaction(transactionId); + if (backendAvailable && server) { + // Very sophisticated way to check if the server is already configured in proxy + if (backendAvailable.data.forwardfor.enabled === 'enabled') { + if (backendAvailable.data.name === domain) { + if (server.data.check === 'enabled') { + if (server.data.address === id) { + if (server.data.port === port) { + serverConfigured = true; + } + } + } + } + } + } } catch (error) {} - try { - const transactionId = await getNextTransactionId(); - await haproxy.post('v2/services/haproxy/configuration/backends', { - searchParams: { - transaction_id: transactionId - }, - json: { - 'init-addr': 'last,libc,none', - forwardfor: { enabled: 'enabled' }, - name: domain - } - }); - await haproxy.post('v2/services/haproxy/configuration/servers', { - searchParams: { - transaction_id: transactionId, - backend: domain - }, - json: { - address: id, - check: 'enabled', - name: id, - port: port - } - }); - console.log({ + if (serverConfigured) return; + const transactionId = await getNextTransactionId(); + await haproxy.post('v2/services/haproxy/configuration/backends', { + searchParams: { + transaction_id: transactionId + }, + json: { + 'init-addr': 'last,libc,none', + forwardfor: { enabled: 'enabled' }, + name: domain + } + }); + await haproxy.post('v2/services/haproxy/configuration/servers', { + searchParams: { + transaction_id: transactionId, + backend: domain + }, + json: { address: id, check: 'enabled', name: id, port: port - }); - await completeTransaction(transactionId); - } catch (error) { - console.log(error); - } + } + }); + await completeTransaction(transactionId); } export async function configureSimpleServiceProxyOff({ domain }) { @@ -605,7 +617,7 @@ export async function removeWwwRedirection(domain) { export async function setWwwRedirection(fqdn) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const transactionId = await getNextTransactionId(); + let transactionId; try { const domain = getDomain(fqdn); @@ -629,6 +641,7 @@ export async function setWwwRedirection(fqdn) { nextRule = rules.data[rules.data.length - 1].index + 1; } const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + transactionId = await getNextTransactionId(); await haproxy .post(`v2/services/haproxy/configuration/http_request_rules`, { searchParams: { @@ -651,6 +664,6 @@ export async function setWwwRedirection(fqdn) { console.log(error); throw error; } finally { - await completeTransaction(transactionId); + if (transactionId) await completeTransaction(transactionId); } } diff --git a/src/lib/queues/cleanup.ts b/src/lib/queues/cleanup.ts index e7bd31e1f..954d3fa67 100644 --- a/src/lib/queues/cleanup.ts +++ b/src/lib/queues/cleanup.ts @@ -23,7 +23,7 @@ export default async function () { ]; for (const image of images) { await asyncExecShell( - `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.managed="true" -t "${image}" -` + `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -` ); } } catch (error) {} @@ -35,11 +35,17 @@ export default async function () { // Cleanup images that are not managed by coolify try { await asyncExecShell( - `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.managed=true' -a -f` + `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f` ); } catch (error) { console.log(error); } + // Cleanup dangling images + try { + await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`); + } catch (error) { + console.log(error); + } } } } diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index ebe154051..f8525c4e0 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -87,7 +87,7 @@ const cron = async () => { await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } }); // await queue.ssl.add('ssl', {}, { repeat: { every: 10000 } }); - await queue.cleanup.add('cleanup', {}, { repeat: { every: 3600000 } }); + if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } }); await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); const events = { @@ -144,7 +144,7 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => { }); }); -const buildLogQueueName = dev ? cuid() : 'log_queue'; +const buildLogQueueName = 'log_queue'; const buildLogQueue = new Queue(buildLogQueueName, connectionOptions); const buildLogWorker = new Worker(buildLogQueueName, async (job) => await logger(job), { concurrency: 1, diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index 9ef0e85f9..92d14f738 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -1,14 +1,16 @@ import { getDomain } from '$lib/common'; -import { getApplicationById, prisma } from '$lib/database'; +import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$lib/database'; import { dockerInstance } from '$lib/docker'; import { checkContainer, configureCoolifyProxyOn, configureProxyForApplication, + configureSimpleServiceProxyOn, forceSSLOnApplication, reloadHaproxy, setWwwRedirection, - startCoolifyProxy + startCoolifyProxy, + startHttpProxy } from '$lib/haproxy'; import * as db from '$lib/database'; @@ -24,39 +26,76 @@ export default async function () { (container) => container.Labels['coolify.managed'] ); for (const configuration of configurations) { - const parsedConfiguration = JSON.parse( - Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString() - ); - if (configuration.Labels['coolify.type'] === 'standalone-application') { - const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration; - if (fqdn) { - const found = await getApplicationById({ id: applicationId }); - if (found) { - const domain = getDomain(fqdn); - await configureProxyForApplication({ - domain, - imageId: pullmergeRequestId - ? `${applicationId}-${pullmergeRequestId}` - : applicationId, - applicationId, - port - }); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) await forceSSLOnApplication({ domain }); - await setWwwRedirection(fqdn); + if (configuration.Labels['coolify.configuration']) { + const parsedConfiguration = JSON.parse( + Buffer.from(configuration.Labels['coolify.configuration'], 'base64').toString() + ); + if ( + parsedConfiguration && + configuration.Labels['coolify.type'] === 'standalone-application' + ) { + const { fqdn, applicationId, port, pullmergeRequestId } = parsedConfiguration; + if (fqdn) { + const found = await getApplicationById({ id: applicationId }); + if (found) { + const domain = getDomain(fqdn); + await configureProxyForApplication({ + domain, + imageId: pullmergeRequestId + ? `${applicationId}-${pullmergeRequestId}` + : applicationId, + applicationId, + port + }); + const isHttps = fqdn.startsWith('https://'); + if (isHttps) await forceSSLOnApplication({ domain }); + await setWwwRedirection(fqdn); + } + } + } + } + } + for (const container of containers) { + const image = container.Image.split(':')[0]; + const found = supportedServiceTypesAndVersions.find((a) => a.baseImage === image); + if (found) { + const type = found.name; + const mainPort = found.ports.main; + const id = container.Names[0].replace('/', ''); + const service = await db.prisma.service.findUnique({ + where: { id }, + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); + const { fqdn } = service; + const domain = getDomain(fqdn); + await configureSimpleServiceProxyOn({ id, domain, port: mainPort }); + const publicPort = service[type]?.publicPort; + if (publicPort) { + const containerFound = await checkContainer( + destination.engine, + `haproxy-for-${publicPort}` + ); + if (!containerFound) { + await startHttpProxy(destination, id, publicPort, 9000); } } } } } } + const services = await prisma.service.findMany({}); // Check Coolify FQDN and configure proxy if needed const { fqdn } = await db.listSettings(); if (fqdn) { const domain = getDomain(fqdn); - const found = await checkContainer('/var/run/docker.sock', 'coolify-haproxy'); - if (!found) await startCoolifyProxy('/var/run/docker.sock'); - await configureCoolifyProxyOn({ domain }); + await startCoolifyProxy('/var/run/docker.sock'); + await configureCoolifyProxyOn(fqdn); await setWwwRedirection(fqdn); const isHttps = fqdn.startsWith('https://'); if (isHttps) await forceSSLOnApplication({ domain }); @@ -64,7 +103,5 @@ export default async function () { } catch (error) { console.log(error); throw error; - } finally { - // await reloadHaproxy('/var/run/docker.sock'); } } diff --git a/src/routes/destinations/[id]/_LocalDocker.svelte b/src/routes/destinations/[id]/_LocalDocker.svelte index 76d2c41bd..4d584e90a 100644 --- a/src/routes/destinations/[id]/_LocalDocker.svelte +++ b/src/routes/destinations/[id]/_LocalDocker.svelte @@ -14,6 +14,7 @@ let cannotDisable = settings.fqdn && destination.engine === '/var/run/docker.sock'; // let scannedApps = []; let loading = false; + let restarting = false; async function handleSubmit() { loading = true; try { @@ -42,6 +43,17 @@ } catch ({ error }) { return errorNotification(error); } + } else if (state === true && destination.isCoolifyProxyUsed === false) { + destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed; + try { + await post(`/destinations/${id}/settings.json`, { + isCoolifyProxyUsed: destination.isCoolifyProxyUsed, + engine: destination.engine + }); + await startProxy(); + } catch ({ error }) { + return errorNotification(error); + } } }); async function changeProxySetting() { @@ -89,6 +101,25 @@ return errorNotification(error); } } + async function forceRestartProxy() { + const sure = confirm( + 'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.' + ); + if (sure) { + try { + restarting = true; + toast.push('Coolify Proxy restarting...'); + await post(`/destinations/${id}/restart.json`, { + engine: destination.engine, + fqdn: settings.fqdn + }); + } catch ({ error }) { + setTimeout(() => { + window.location.reload(); + }, 5000); + } + } + }
@@ -103,6 +134,12 @@ disabled={loading} >{loading ? 'Saving...' : 'Save'} + diff --git a/src/routes/destinations/[id]/restart.json.ts b/src/routes/destinations/[id]/restart.json.ts new file mode 100644 index 000000000..b3efa60da --- /dev/null +++ b/src/routes/destinations/[id]/restart.json.ts @@ -0,0 +1,34 @@ +import { getDomain, getUserDetails } from '$lib/common'; +import { ErrorHandler } from '$lib/database'; +import * as db from '$lib/database'; +import { + configureCoolifyProxyOn, + forceSSLOnApplication, + setWwwRedirection, + startCoolifyProxy, + stopCoolifyProxy +} from '$lib/haproxy'; +import type { RequestHandler } from '@sveltejs/kit'; + +export const post: RequestHandler = async (event) => { + const { teamId, status, body } = await getUserDetails(event); + if (status === 401) return { status, body }; + + const { engine, fqdn } = await event.request.json(); + + try { + const domain = getDomain(fqdn); + await stopCoolifyProxy(engine); + await startCoolifyProxy(engine); + await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true }); + await configureCoolifyProxyOn(fqdn); + await setWwwRedirection(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isHttps) await forceSSLOnApplication({ domain }); + return { + status: 200 + }; + } catch (error) { + return ErrorHandler(error); + } +}; diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index c08c18e54..e865b5e52 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -15,6 +15,7 @@ import { import getPort from 'get-port'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -63,7 +64,8 @@ export const post: RequestHandler = async (event) => { environment: config.environmentVariables, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('minio') } }, networks: { diff --git a/src/routes/services/[id]/nocodb/start.json.ts b/src/routes/services/[id]/nocodb/start.json.ts index 3ce559e45..e988c7eb4 100644 --- a/src/routes/services/[id]/nocodb/start.json.ts +++ b/src/routes/services/[id]/nocodb/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -39,7 +40,8 @@ export const post: RequestHandler = async (event) => { container_name: id, image: `nocodb/nocodb:${version}`, networks: [network], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('nocodb') } }, networks: { diff --git a/src/routes/services/[id]/plausibleanalytics/start.json.ts b/src/routes/services/[id]/plausibleanalytics/start.json.ts index 96dbac3c2..274c59bd9 100644 --- a/src/routes/services/[id]/plausibleanalytics/start.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -82,7 +83,6 @@ export const post: RequestHandler = async (event) => { const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); const engine = destinationDocker.engine; - // const labels = await makeLabelForPlausibleAnalytics({ id, }) const { workdir } = await createDirectories({ repository: type, buildId: id }); @@ -138,7 +138,8 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`; environment: config.plausibleAnalytics.environmentVariables, volumes: [config.postgresql.volume], restart: 'always', - depends_on: [`${id}-postgresql`, `${id}-clickhouse`] + depends_on: [`${id}-postgresql`, `${id}-clickhouse`], + labels: makeLabelForServices('plausibleAnalytics') }, [`${id}-postgresql`]: { container_name: `${id}-postgresql`, diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts index ab05590a8..4b6e3637a 100644 --- a/src/routes/services/[id]/vaultwarden/start.json.ts +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { getServiceImage, ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -46,7 +47,8 @@ export const post: RequestHandler = async (event) => { image: config.image, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('vaultWarden') } }, networks: { diff --git a/src/routes/services/[id]/vscodeserver/start.json.ts b/src/routes/services/[id]/vscodeserver/start.json.ts index 4709a823e..fb49b1b7a 100644 --- a/src/routes/services/[id]/vscodeserver/start.json.ts +++ b/src/routes/services/[id]/vscodeserver/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -54,7 +55,8 @@ export const post: RequestHandler = async (event) => { environment: config.environmentVariables, networks: [network], volumes: [config.volume], - restart: 'always' + restart: 'always', + labels: makeLabelForServices('vscodeServer') } }, networks: { diff --git a/src/routes/services/[id]/wordpress/start.json.ts b/src/routes/services/[id]/wordpress/start.json.ts index 2ed3039cd..e7c305deb 100644 --- a/src/routes/services/[id]/wordpress/start.json.ts +++ b/src/routes/services/[id]/wordpress/start.json.ts @@ -12,6 +12,7 @@ import { } from '$lib/haproxy'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; +import { makeLabelForServices } from '$lib/buildPacks/common'; export const post: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -78,7 +79,8 @@ export const post: RequestHandler = async (event) => { environment: config.wordpress.environmentVariables, networks: [network], restart: 'always', - depends_on: [`${id}-mysql`] + depends_on: [`${id}-mysql`], + labels: makeLabelForServices('wordpress') }, [`${id}-mysql`]: { container_name: `${id}-mysql`, diff --git a/src/routes/settings/index.json.ts b/src/routes/settings/index.json.ts index cad240233..cdf3ee85d 100644 --- a/src/routes/settings/index.json.ts +++ b/src/routes/settings/index.json.ts @@ -87,8 +87,7 @@ export const post: RequestHandler = async (event) => { } } if (fqdn) { - const found = await checkContainer('/var/run/docker.sock', 'coolify-haproxy'); - if (!found) await startCoolifyProxy('/var/run/docker.sock'); + await startCoolifyProxy('/var/run/docker.sock'); const domain = getDomain(fqdn); const isHttps = fqdn.startsWith('https://'); if (domain) {