From 02abd038fada274c200498c93e09363a811beeaa Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 28 Feb 2022 11:20:46 +0100 Subject: [PATCH 01/20] fix: Better proxy check --- src/lib/haproxy/index.ts | 52 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index fffba0ba7..47c9c85e1 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -199,6 +199,7 @@ export async function checkProxyConfigurations() { backend_name: backendName, stats: { lastchg } } = stat; + const { fqdn } = await db.listSettings(); if (fqdn) { const domain = getDomain(fqdn); @@ -206,8 +207,15 @@ export async function checkProxyConfigurations() { return; } } - const application = await db.getApplicationById(name); - if (!application) { + const application = await db.prisma.application.findUnique({ + where: { id: name }, + include: { destinationDocker: true } + }); + const service = await db.prisma.service.findUnique({ + where: { id: name }, + include: { destinationDocker: true } + }); + if (!application && !service) { const transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { @@ -218,18 +226,36 @@ export async function checkProxyConfigurations() { .json(); return await completeTransaction(transactionId); } - const found = await checkContainer(application.destinationDocker.engine, name); - if (!found) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - return await completeTransaction(transactionId); + if (application?.destinationDocker?.engine && lastchg > 120) { + const found = await checkContainer(application.destinationDocker.engine, name); + if (!found) { + const transactionId = await getNextTransactionId(); + await haproxy + .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { + searchParams: { + transaction_id: transactionId + } + }) + .json(); + return await completeTransaction(transactionId); + } } + + if (service?.destinationDocker?.engine && lastchg > 120) { + const found = await checkContainer(service.destinationDocker.engine, name); + if (!found) { + const transactionId = await getNextTransactionId(); + await haproxy + .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { + searchParams: { + transaction_id: transactionId + } + }) + .json(); + return await completeTransaction(transactionId); + } + } + if (lastchg > 120) { const transactionId = await getNextTransactionId(); await haproxy From f340ca9d0587b0921983f3d2b6d9f14dc8c8b751 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 28 Feb 2022 16:06:44 +0100 Subject: [PATCH 02/20] WIP --- src/lib/haproxy/index.ts | 486 ++++++++++++------ src/lib/importers/github.ts | 2 +- src/lib/letsencrypt.ts | 7 +- src/lib/queues/builder.ts | 49 +- src/lib/queues/index.ts | 4 +- src/lib/queues/proxy.ts | 127 ++--- src/routes/applications/[id]/stop.json.ts | 2 +- src/routes/services/[id]/minio/start.json.ts | 16 +- src/routes/services/[id]/minio/stop.json.ts | 2 +- src/routes/services/[id]/nocodb/start.json.ts | 14 +- .../[id]/plausibleanalytics/start.json.ts | 14 +- .../services/[id]/vaultwarden/start.json.ts | 14 +- .../services/[id]/vscodeserver/start.json.ts | 14 +- .../services/[id]/wordpress/start.json.ts | 14 +- 14 files changed, 445 insertions(+), 320 deletions(-) diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 47c9c85e1..90f81e34d 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -2,6 +2,7 @@ import { dev } from '$app/env'; import { asyncExecShell, getDomain, getEngine } from '$lib/common'; import got from 'got'; import * as db from '$lib/database'; +import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; @@ -71,7 +72,9 @@ export async function removeProxyConfiguration(fqdn) { export async function forceSSLOffApplication(domain) { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); + let transactionId; + try { const rules: any = await haproxy .get(`v2/services/haproxy/configuration/http_request_rules`, { @@ -87,7 +90,6 @@ export async function forceSSLOffApplication(domain) { ); if (rule) { transactionId = await getNextTransactionId(); - await haproxy .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { searchParams: { @@ -188,168 +190,325 @@ export async function reloadHaproxy(engine) { return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`); } export async function checkProxyConfigurations() { + const timeout = 10; const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); try { const stats: any = await haproxy.get(`v2/services/haproxy/stats/native`).json(); + let transactionId = null; for (const stat of stats[0].stats) { - if (stat.stats.status === 'DOWN' && stat.type === 'server') { - const { - name, - backend_name: backendName, - stats: { lastchg } - } = stat; - - const { fqdn } = await db.listSettings(); - if (fqdn) { - const domain = getDomain(fqdn); - if (backendName === domain) { - return; - } - } - const application = await db.prisma.application.findUnique({ - where: { id: name }, - include: { destinationDocker: true } - }); - const service = await db.prisma.service.findUnique({ - where: { id: name }, - include: { destinationDocker: true } - }); - if (!application && !service) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - return await completeTransaction(transactionId); - } - if (application?.destinationDocker?.engine && lastchg > 120) { - const found = await checkContainer(application.destinationDocker.engine, name); - if (!found) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - return await completeTransaction(transactionId); - } - } - - if (service?.destinationDocker?.engine && lastchg > 120) { - const found = await checkContainer(service.destinationDocker.engine, name); - if (!found) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - return await completeTransaction(transactionId); - } - } - - if (lastchg > 120) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - await completeTransaction(transactionId); - } + if (stat.stats.status !== 'no check' && stat.type === 'server') { + if (!transactionId) await getNextTransactionId(); + const { backend_name: backendName } = stat; + await haproxy + .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { + searchParams: { + transaction_id: transactionId + } + }) + .json(); } } + if (transactionId) await completeTransaction(transactionId); } catch (error) { - console.log(error); + console.log(error.response.body); } } -export async function configureProxyForApplication({ domain, imageId, applicationId, port }) { +export async function configureHAProxy(fqdn, id, port, containerRunning, engine) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; + + console.log({ application: true, fqdn, domain, id, port, containerRunning, isHttps, isWWW }); + const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - let serverConfigured = false; - let backendAvailable: any = null; + let transactionId; - try { - backendAvailable = await haproxy - .get(`v2/services/haproxy/configuration/backends/${domain}`) - .json(); - const server: any = await haproxy - .get(`v2/services/haproxy/configuration/servers/${imageId}`, { - searchParams: { - backend: domain + if (!containerRunning) { + try { + await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); + transactionId = await getNextTransactionId(); + await haproxy + .delete(`v2/services/haproxy/configuration/backends/${domain}`, { + searchParams: { + transaction_id: transactionId + } + }) + .json(); + } catch (error) { + // + } + try { + if (!transactionId) await getNextTransactionId(); + let rules: any; + // Force SSL off + rules = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => + rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) + ); + if (rule) { + await haproxy + .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); } - }) - .json(); + } - 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 === imageId) { - if (server.data.port === port) { - serverConfigured = true; + // Force WWW off + rules = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); + if (rule) { + await haproxy + .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + } + } + } catch (error) { + console.log(error); + // + } finally { + try { + if (transactionId) { + console.log(transactionId); + await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); + } + } catch (error) { + if (error?.response?.body) { + const json = JSON.parse(error.response.body); + if (json.code === 400 && json.message.includes('could not resolve address')) { + await stopCoolifyProxy(engine); + await startCoolifyProxy(engine); + } + } + } + } + return; + } else { + console.log('adding ', domain); + let serverConfigured = false; + let backendAvailable: any = null; + try { + backendAvailable = await haproxy + .get(`v2/services/haproxy/configuration/backends/${domain}`) + .json(); + const server: any = await haproxy + .get(`v2/services/haproxy/configuration/servers/${id}`, { + searchParams: { + backend: domain + } + }) + .json(); + + 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 === 'disabled') { + if (server.data.address === id) { + if (server.data.port === port) { + serverConfigured = true; + } } } } } } + } catch (error) { + // } - } catch (error) { - //console.log('error getting backend or server', error?.response?.body); - // - } - - if (serverConfigured) return; - const transactionId = await getNextTransactionId(); - if (backendAvailable) { - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { + if (serverConfigured) { + console.log('server configured'); + return; + } + if (!transactionId) transactionId = await getNextTransactionId(); + if (backendAvailable) { + await haproxy + .delete(`v2/services/haproxy/configuration/backends/${domain}`, { + searchParams: { + transaction_id: transactionId + } + }) + .json(); + } + try { + console.log('adding ', domain); + await haproxy.post('v2/services/haproxy/configuration/backends', { searchParams: { transaction_id: transactionId + }, + json: { + 'init-addr': 'last,libc,none', + forwardfor: { enabled: 'enabled' }, + name: domain } - }) - .json(); - } - try { - 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: imageId, - check: 'enabled', - name: imageId, - port: port + await haproxy.post('v2/services/haproxy/configuration/servers', { + searchParams: { + transaction_id: transactionId, + backend: domain + }, + json: { + address: id, + check: 'disabled', + name: id, + port: port + } + }); + + let rules: any; + + // Force SSL off + rules = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => + rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) + ); + if (rule) { + await haproxy + .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + } } - }); - } catch (error) { - throw error?.response?.body || error; - } finally { - await completeTransaction(transactionId); + + // Generate SSL && force SSL on + + if (isHttps) { + await letsEncrypt(domain, id, false); + rules = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + let nextRule = 0; + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => + rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) + ); + if (rule) return; + nextRule = rules.data[rules.data.length - 1].index + 1; + } + + await haproxy + .post(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + }, + json: { + index: nextRule, + cond: 'if', + cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, + type: 'redirect', + redir_type: 'scheme', + redir_value: 'https', + redir_code: dev ? 302 : 301 + } + }) + .json(); + } + + // WWW redirect on + rules = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + let nextRule = 0; + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); + if (rule) return; + nextRule = rules.data[rules.data.length - 1].index + 1; + } + + await haproxy + .post(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + }, + json: { + index: nextRule, + cond: 'if', + cond_test: contTest, + type: 'redirect', + redir_type: 'location', + redir_value: redirectValue, + redir_code: dev ? 302 : 301 + } + }) + .json(); + } catch (error) { + console.log(error); + throw error?.response?.body || error; + } finally { + try { + if (transactionId) { + console.log('Committing transaction'); + await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); + } + } catch (error) { + if (error?.response?.body) { + const json = JSON.parse(error.response.body); + if (json.code === 400 && json.message.includes('could not resolve address')) { + await stopCoolifyProxy(engine); + await startCoolifyProxy(engine); + } + } + } + } } } @@ -444,7 +603,7 @@ export async function configureCoolifyProxyOn(fqdn) { }, json: { address: dev ? 'host.docker.internal' : 'coolify', - check: 'enabled', + check: 'enable', fall: 10, name: 'coolify', port: 3000 @@ -587,6 +746,7 @@ export async function configureNetworkCoolifyProxy(engine) { } export async function configureSimpleServiceProxyOn({ id, domain, port }) { + console.log({ service: true, id, domain, port }); const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); let serverConfigured = false; @@ -637,7 +797,7 @@ export async function configureSimpleServiceProxyOn({ id, domain, port }) { }, json: { address: id, - check: 'enabled', + check: 'enable', name: id, port: port } @@ -676,29 +836,37 @@ export async function removeWwwRedirection(fqdn) { const haproxy = await haproxyInstance(); await checkHAProxy(); - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' + + let transactionId; + + try { + const rules: any = await haproxy + .get(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); + if (rules.data.length > 0) { + const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); + if (rule) { + transactionId = await getNextTransactionId(); + await haproxy + .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + } + }) + .json(); } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); - if (rule) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - await completeTransaction(transactionId); } + } catch (error) { + console.log(error); + } finally { + if (transactionId) await completeTransaction(transactionId); } } export async function setWwwRedirection(fqdn) { diff --git a/src/lib/importers/github.ts b/src/lib/importers/github.ts index 418fe596a..608b6bd36 100644 --- a/src/lib/importers/github.ts +++ b/src/lib/importers/github.ts @@ -14,7 +14,7 @@ export default async function ({ buildId }): Promise { try { - saveBuildLog({ line: 'GitHub importer started', buildId, applicationId }); + saveBuildLog({ line: 'GitHub importer started.', buildId, applicationId }); const { privateKey, appId, installationId } = await db.getUniqueGithubApp({ githubAppId }); const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, ''); diff --git a/src/lib/letsencrypt.ts b/src/lib/letsencrypt.ts index d70de1812..c062287ea 100644 --- a/src/lib/letsencrypt.ts +++ b/src/lib/letsencrypt.ts @@ -5,7 +5,7 @@ import * as db from '$lib/database'; import cuid from 'cuid'; import getPort, { portNumbers } from 'get-port'; -export async function letsEncrypt({ domain, isCoolify = false, id = null }) { +export async function letsEncrypt(domain, id = null, isCoolify = false) { try { const data = await db.prisma.setting.findFirst(); const { minPort, maxPort } = data; @@ -47,7 +47,6 @@ export async function letsEncrypt({ domain, isCoolify = false, id = null }) { } } } - await forceSSLOffApplication(domain); if (dualCerts) { await asyncExecShell( `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${randomPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${randomPort} -d ${nakedDomain} -d ${wwwDomain} --expand --agree-tos --non-interactive --register-unsafely-without-email ${ @@ -71,9 +70,5 @@ export async function letsEncrypt({ domain, isCoolify = false, id = null }) { if (error.code !== 0) { throw error; } - } finally { - if (!isCoolify) { - await forceSSLOnApplication(domain); - } } } diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 4c1ad631e..0de934bef 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -4,12 +4,6 @@ import * as buildpacks from '../buildPacks'; import * as importers from '../importers'; import { dockerInstance } from '../docker'; import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog } from '../common'; -import { - checkProxyConfigurations, - configureProxyForApplication, - reloadHaproxy, - setWwwRedirection -} from '../haproxy'; import * as db from '$lib/database'; import { decrypt } from '$lib/crypto'; import { sentry } from '$lib/common'; @@ -261,26 +255,27 @@ export default async function (job) { sentry.captureException(error); throw new Error(error); } - try { - if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { - saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId }); - await checkProxyConfigurations(); - await configureProxyForApplication({ domain, imageId, applicationId, port }); - if (isHttps) await letsEncrypt({ domain, id: applicationId }); - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); - saveBuildLog({ line: 'Proxy configuration successful!', buildId, applicationId }); - } else { - saveBuildLog({ - line: 'Coolify Proxy is not configured for this destination. Nothing else to do.', - buildId, - applicationId - }); - } - } catch (error) { - saveBuildLog({ line: error.stdout || error, buildId, applicationId }); - sentry.captureException(error); - throw new Error(error); - } + saveBuildLog({ line: 'Proxy will be configured shortly.', buildId, applicationId }); + // try { + // if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { + // saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId }); + // await checkProxyConfigurations(); + // await configureProxyForApplication(domain, imageId, port); + // if (isHttps) await letsEncrypt({ domain, id: applicationId }); + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); + // saveBuildLog({ line: 'Proxy configuration successful!', buildId, applicationId }); + // } else { + // saveBuildLog({ + // line: 'Coolify Proxy is not configured for this destination. Nothing else to do.', + // buildId, + // applicationId + // }); + // } + // } catch (error) { + // saveBuildLog({ line: error.stdout || error, buildId, applicationId }); + // sentry.captureException(error); + // throw new Error(error); + // } } } diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index 45c9c8a52..28a080ed3 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -87,8 +87,8 @@ 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: 600000 } }); - await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); + // await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } }); + // await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); const events = { proxy: new QueueEvents('proxy', { ...connectionOptions }), diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index 074ac55ab..cb6aaf2cc 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -1,20 +1,15 @@ +import * as db from '$lib/database'; import { getDomain } from '$lib/common'; -import { getApplicationById, prisma, supportedServiceTypesAndVersions } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; import { checkContainer, checkProxyConfigurations, configureCoolifyProxyOn, - configureProxyForApplication, - configureSimpleServiceProxyOn, + configureHAProxy, forceSSLOnApplication, - reloadHaproxy, setWwwRedirection, startCoolifyProxy, startHttpProxy } from '$lib/haproxy'; -import * as db from '$lib/database'; -// import { generateRemoteEngine } from '$lib/components/common'; export default async function () { try { @@ -23,83 +18,55 @@ export default async function () { console.log(error); } try { - // Check destination containers and configure proxy if needed - const destinationDockers = await prisma.destinationDocker.findMany({}); - for (const destination of destinationDockers) { - if (destination.isCoolifyProxyUsed) { - // if (destination.remoteEngine) { - // const engine = generateRemoteEngine(destination); - // } - const docker = dockerInstance({ destinationDocker: destination }); - const containers = await docker.engine.listContainers(); - const configurations = containers.filter( - (container) => container.Labels['coolify.managed'] - ); - for (const configuration of configurations) { - 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 applications = await db.prisma.application.findMany({ + include: { destinationDocker: true } + }); + + for (const application of applications) { + const { + fqdn, + id, + port, + destinationDocker: { engine } + } = application; + const containerRunning = await checkContainer(engine, id); + await configureHAProxy(fqdn, id, port, containerRunning, engine); + } + + const services = await db.prisma.service.findMany({ + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); + + for (const service of services) { + const { + fqdn, + id, + type, + destinationDocker: { engine } + } = service; + const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + if (found) { + const port = found.ports.main; + const publicPort = service[type]?.publicPort; + const containerRunning = await checkContainer(engine, id); + await configureHAProxy(fqdn, id, port, containerRunning, engine); + if (publicPort) { + const containerFound = await checkContainer( + service.destinationDocker.engine, + `haproxy-for-${publicPort}` + ); + if (!containerFound) { + await startHttpProxy(service.destinationDocker, id, publicPort, 9000); } } } } - const services = await prisma.service.findMany({}); // Check Coolify FQDN and configure proxy if needed const { fqdn } = await db.listSettings(); if (fqdn) { diff --git a/src/routes/applications/[id]/stop.json.ts b/src/routes/applications/[id]/stop.json.ts index c9d5b9364..58064c07a 100644 --- a/src/routes/applications/[id]/stop.json.ts +++ b/src/routes/applications/[id]/stop.json.ts @@ -20,7 +20,7 @@ export const post: RequestHandler = async (event) => { const docker = dockerInstance({ destinationDocker }); await docker.engine.getContainer(id).stop(); } - await removeProxyConfiguration(fqdn); + // await removeProxyConfiguration(fqdn); return { status: 200 }; diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index 78372470a..1d1a0655e 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -24,7 +24,7 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); + // await checkHAProxy(); const service = await db.getService({ id, teamId }); const { type, @@ -96,16 +96,16 @@ export const post: RequestHandler = async (event) => { } try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: consolePort }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: consolePort }); await db.updateMinioService({ id, publicPort }); await startHttpProxy(destinationDocker, id, publicPort, apiPort); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/minio/stop.json.ts b/src/routes/services/[id]/minio/stop.json.ts index aed702b1b..0285e776c 100644 --- a/src/routes/services/[id]/minio/stop.json.ts +++ b/src/routes/services/[id]/minio/stop.json.ts @@ -35,7 +35,7 @@ export const post: RequestHandler = async (event) => { } try { await stopTcpHttpProxy(destinationDocker, publicPort); - await configureSimpleServiceProxyOff(fqdn); + // await configureSimpleServiceProxyOff(fqdn); } catch (error) { console.log(error); } diff --git a/src/routes/services/[id]/nocodb/start.json.ts b/src/routes/services/[id]/nocodb/start.json.ts index b6606be7b..82ccd2a1c 100644 --- a/src/routes/services/[id]/nocodb/start.json.ts +++ b/src/routes/services/[id]/nocodb/start.json.ts @@ -56,14 +56,14 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/plausibleanalytics/start.json.ts b/src/routes/services/[id]/plausibleanalytics/start.json.ts index 14c4bf934..fe0171267 100644 --- a/src/routes/services/[id]/plausibleanalytics/start.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/start.json.ts @@ -187,14 +187,14 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`; await asyncExecShell( `DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d` ); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: 8000 }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: 8000 }); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts index 37482faef..5dcacbea3 100644 --- a/src/routes/services/[id]/vaultwarden/start.json.ts +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -74,14 +74,14 @@ export const post: RequestHandler = async (event) => { } try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: 80 }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: 80 }); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/vscodeserver/start.json.ts b/src/routes/services/[id]/vscodeserver/start.json.ts index 8bd06b17c..ee5f2670b 100644 --- a/src/routes/services/[id]/vscodeserver/start.json.ts +++ b/src/routes/services/[id]/vscodeserver/start.json.ts @@ -84,14 +84,14 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/wordpress/start.json.ts b/src/routes/services/[id]/wordpress/start.json.ts index fde5cfe66..76e113bce 100644 --- a/src/routes/services/[id]/wordpress/start.json.ts +++ b/src/routes/services/[id]/wordpress/start.json.ts @@ -121,14 +121,14 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - await checkProxyConfigurations(); - await configureSimpleServiceProxyOn({ id, domain, port: 80 }); + // await checkProxyConfigurations(); + // await configureSimpleServiceProxyOn({ id, domain, port: 80 }); - if (isHttps) { - await letsEncrypt({ domain, id }); - } - await setWwwRedirection(fqdn); - await reloadHaproxy(destinationDocker.engine); + // if (isHttps) { + // await letsEncrypt({ domain, id }); + // } + // await setWwwRedirection(fqdn); + // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; From 2daa0438403feb62b3bfa8d39c430574036028c9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 28 Feb 2022 16:55:02 +0100 Subject: [PATCH 03/20] WIP --- src/lib/haproxy/index.ts | 51 +++++++++++++++++++++------------------- src/lib/queues/proxy.ts | 48 ++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 90f81e34d..a75050cca 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -214,23 +214,27 @@ export async function checkProxyConfigurations() { console.log(error.response.body); } } -export async function configureHAProxy(fqdn, id, port, containerRunning, engine) { +export async function configureHAProxy( + haproxy, + transactionId, + fqdn, + id, + port, + containerRunning, + engine +) { const domain = getDomain(fqdn); const isHttps = fqdn.startsWith('https://'); const isWWW = fqdn.includes('www.'); const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; - console.log({ application: true, fqdn, domain, id, port, containerRunning, isHttps, isWWW }); - - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - - let transactionId; + // console.log({ fqdn, domain, id, port, containerRunning, isHttps, isWWW }); if (!containerRunning) { try { await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); + console.log('removing', domain); transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/backends/${domain}`, { @@ -240,10 +244,16 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) }) .json(); } catch (error) { + if (error?.response?.body) { + const json = JSON.parse(error.response.body); + if (json.code === 400 && json.message.includes('could not resolve address')) { + await stopCoolifyProxy(engine); + await startCoolifyProxy(engine); + } + } // } try { - if (!transactionId) await getNextTransactionId(); let rules: any; // Force SSL off rules = await haproxy @@ -259,6 +269,7 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) ); if (rule) { + if (!transactionId) transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { searchParams: { @@ -283,6 +294,7 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) if (rules.data.length > 0) { const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); if (rule) { + if (!transactionId) transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { searchParams: { @@ -299,10 +311,7 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) // } finally { try { - if (transactionId) { - console.log(transactionId); - await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); - } + if (transactionId) return transactionId; } catch (error) { if (error?.response?.body) { const json = JSON.parse(error.response.body); @@ -315,7 +324,6 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) } return; } else { - console.log('adding ', domain); let serverConfigured = false; let backendAvailable: any = null; try { @@ -346,13 +354,15 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) } } catch (error) { // + console.log(error); } if (serverConfigured) { - console.log('server configured'); + console.log('server configured', domain); return; } - if (!transactionId) transactionId = await getNextTransactionId(); + if (backendAvailable) { + if (!transactionId) transactionId = await getNextTransactionId(); await haproxy .delete(`v2/services/haproxy/configuration/backends/${domain}`, { searchParams: { @@ -363,6 +373,7 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) } try { console.log('adding ', domain); + if (!transactionId) transactionId = await getNextTransactionId(); await haproxy.post('v2/services/haproxy/configuration/backends', { searchParams: { transaction_id: transactionId @@ -373,7 +384,6 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) name: domain } }); - await haproxy.post('v2/services/haproxy/configuration/servers', { searchParams: { transaction_id: transactionId, @@ -386,7 +396,6 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) port: port } }); - let rules: any; // Force SSL off @@ -414,9 +423,7 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) .json(); } } - // Generate SSL && force SSL on - if (isHttps) { await letsEncrypt(domain, id, false); rules = await haproxy @@ -492,13 +499,9 @@ export async function configureHAProxy(fqdn, id, port, containerRunning, engine) .json(); } catch (error) { console.log(error); - throw error?.response?.body || error; } finally { try { - if (transactionId) { - console.log('Committing transaction'); - await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); - } + if (transactionId) return transactionId; } catch (error) { if (error?.response?.body) { const json = JSON.parse(error.response.body); diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index cb6aaf2cc..a72cf6cb3 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -2,16 +2,21 @@ import * as db from '$lib/database'; import { getDomain } from '$lib/common'; import { checkContainer, + checkHAProxy, checkProxyConfigurations, configureCoolifyProxyOn, configureHAProxy, forceSSLOnApplication, + haproxyInstance, setWwwRedirection, startCoolifyProxy, startHttpProxy } from '$lib/haproxy'; export default async function () { + const haproxy = await haproxyInstance(); + await checkHAProxy(haproxy); + let transactionId; try { await checkProxyConfigurations(); } catch (error) { @@ -30,7 +35,15 @@ export default async function () { destinationDocker: { engine } } = application; const containerRunning = await checkContainer(engine, id); - await configureHAProxy(fqdn, id, port, containerRunning, engine); + transactionId = await configureHAProxy( + haproxy, + transactionId, + fqdn, + id, + port, + containerRunning, + engine + ); } const services = await db.prisma.service.findMany({ @@ -50,12 +63,23 @@ export default async function () { type, destinationDocker: { engine } } = service; + console.log({ fqdn, id, type, engine }); const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); if (found) { + console.log(found); const port = found.ports.main; const publicPort = service[type]?.publicPort; const containerRunning = await checkContainer(engine, id); - await configureHAProxy(fqdn, id, port, containerRunning, engine); + console.log(containerRunning); + transactionId = await configureHAProxy( + haproxy, + transactionId, + fqdn, + id, + port, + containerRunning, + engine + ); if (publicPort) { const containerFound = await checkContainer( service.destinationDocker.engine, @@ -67,16 +91,18 @@ export default async function () { } } } + console.log(transactionId); + if (transactionId) await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); // Check Coolify FQDN and configure proxy if needed - const { fqdn } = await db.listSettings(); - if (fqdn) { - const domain = getDomain(fqdn); - await startCoolifyProxy('/var/run/docker.sock'); - await configureCoolifyProxyOn(fqdn); - await setWwwRedirection(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) await forceSSLOnApplication(domain); - } + // const { fqdn } = await db.listSettings(); + // if (fqdn) { + // const domain = getDomain(fqdn); + // await startCoolifyProxy('/var/run/docker.sock'); + // await configureCoolifyProxyOn(fqdn); + // await setWwwRedirection(fqdn); + // const isHttps = fqdn.startsWith('https://'); + // if (isHttps) await forceSSLOnApplication(domain); + // } } catch (error) { throw error; } From 69d1556a1dd785de01732f534ff85defb74fd425 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 00:08:54 +0100 Subject: [PATCH 04/20] WIP better automatic proxy conf --- package.json | 1 + pnpm-lock.yaml | 10 + prisma/schema.prisma | 1 + src/lib/haproxy/configuration.ts | 130 +++++++ src/lib/haproxy/index.ts | 582 +++++++++++++++---------------- src/lib/queues/proxy.ts | 195 +++++------ 6 files changed, 528 insertions(+), 391 deletions(-) create mode 100644 src/lib/haproxy/configuration.ts diff --git a/package.json b/package.json index 81ea664eb..ac087f1bc 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "js-cookie": "3.0.1", "js-yaml": "4.1.0", "jsonwebtoken": "8.5.1", + "mustache": "^4.2.0", "node-forge": "1.2.1", "svelte-kit-cookie-session": "2.1.2", "tailwindcss-scrollbar": "^0.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 742b45b03..a9411c6f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,7 @@ specifiers: js-yaml: 4.1.0 jsonwebtoken: 8.5.1 lint-staged: 12.3.4 + mustache: ^4.2.0 node-forge: 1.2.1 postcss: 8.4.6 prettier: 2.5.1 @@ -70,6 +71,7 @@ dependencies: js-cookie: 3.0.1 js-yaml: 4.1.0 jsonwebtoken: 8.5.1 + mustache: 4.2.0 node-forge: 1.2.1 svelte-kit-cookie-session: 2.1.2 tailwindcss-scrollbar: 0.1.0_tailwindcss@3.0.23 @@ -4091,6 +4093,14 @@ packages: msgpackr-extract: 1.0.15 dev: false + /mustache/4.2.0: + resolution: + { + integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + } + hasBin: true + dev: false + /nan/2.15.0: resolution: { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9ec70de70..688e312b7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -16,6 +16,7 @@ model Setting { maxPort Int @default(9100) proxyPassword String proxyUser String + proxyHash String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts new file mode 100644 index 000000000..3792fc5b6 --- /dev/null +++ b/src/lib/haproxy/configuration.ts @@ -0,0 +1,130 @@ +import { dev } from '$app/env'; +import got from 'got'; +import mustache from 'mustache'; +import crypto from 'crypto'; + +import * as db from '$lib/database'; +import { checkContainer, checkHAProxy } from '.'; +import { getDomain } from '$lib/common'; + +const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; + +let template = `#coolhash={{hash}} +program api + command /usr/bin/dataplaneapi -f /usr/local/etc/haproxy/dataplaneapi.hcl --userlist haproxy-dataplaneapi + no option start-on-reload + +global + stats socket /var/run/api.sock user haproxy group haproxy mode 660 level admin expose-fd listeners + log stdout format raw local0 debug + +defaults + mode http + log global + timeout http-request 60s + timeout connect 10s + timeout client 60s + timeout server 60s + +userlist haproxy-dataplaneapi + user admin insecure-password "\${HAPROXY_PASSWORD}" + +frontend http + mode http + bind :80 + bind :443 ssl crt /usr/local/etc/haproxy/ssl/ alpn h2,http/1.1 + acl is_certbot path_beg /.well-known/acme-challenge/ + {{#applications}} + {{#isHttps}} + http-request redirect scheme https code ${ + dev ? 302 : 301 + } if { hdr(host) -i {{domain}} } !{ ssl_fc } + {{/isHttps}} + http-request redirect location {{{redirectValue}}} code ${ + dev ? 302 : 301 + } if { req.hdr(host) -i {{redirectTo}} } + {{/applications}} + use_backend backend-certbot if is_certbot + use_backend %[req.hdr(host),lower] + +frontend stats + bind *:8404 + stats enable + stats uri / + stats refresh 5s + stats admin if TRUE + stats auth "\${HAPROXY_USERNAME}:\${HAPROXY_PASSWORD}" + +backend backend-certbot + mode http + server certbot host.docker.internal:9080 + +{{#applications}} + +backend {{domain}} + option forwardfor + server {{id}} {{id}}:{{port}} +{{/applications}} +`; +export async function haproxyInstance() { + const { proxyPassword } = await db.listSettings(); + return got.extend({ + prefixUrl: url, + username: 'admin', + password: proxyPassword + }); +} + +export async function configureHAProxy() { + const haproxy = await haproxyInstance(); + await checkHAProxy(haproxy); + const data = { + applications: [], + services: [] + }; + const applications = await db.prisma.application.findMany({ + include: { destinationDocker: true } + }); + for (const application of applications) { + const { + fqdn, + id, + port, + destinationDocker: { engine } + } = application; + const isRunning = await checkContainer(engine, id); + if (isRunning) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + data.applications.push({ + id, + port, + domain, + isHttps, + redirectValue, + redirectTo: isWWW ? domain : 'www.' + domain + }); + } + } + const output = mustache.render(template, data); + const newHash = crypto.createHash('md5').update(JSON.stringify(template)).digest('hex'); + const { proxyHash, id } = await db.listSettings(); + console.log(proxyHash, newHash); + if (proxyHash !== newHash) { + await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } }); + console.log('HAProxy configuration changed, updating...'); + await haproxy.post(`v2/services/haproxy/configuration/raw`, { + searchParams: { + skip_version: true + }, + body: output, + headers: { + 'Content-Type': 'text/plain' + } + }); + } else { + console.log('HAProxy configuration is up to date'); + } +} diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index a75050cca..734c3a038 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -214,306 +214,306 @@ export async function checkProxyConfigurations() { console.log(error.response.body); } } -export async function configureHAProxy( - haproxy, - transactionId, - fqdn, - id, - port, - containerRunning, - engine -) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; - const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; +// export async function configureHAProxy( +// haproxy, +// transactionId, +// fqdn, +// id, +// port, +// containerRunning, +// engine +// ) { +// const domain = getDomain(fqdn); +// const isHttps = fqdn.startsWith('https://'); +// const isWWW = fqdn.includes('www.'); +// const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; +// const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; - // console.log({ fqdn, domain, id, port, containerRunning, isHttps, isWWW }); +// // console.log({ fqdn, domain, id, port, containerRunning, isHttps, isWWW }); - if (!containerRunning) { - try { - await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); - console.log('removing', domain); - transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - } catch (error) { - if (error?.response?.body) { - const json = JSON.parse(error.response.body); - if (json.code === 400 && json.message.includes('could not resolve address')) { - await stopCoolifyProxy(engine); - await startCoolifyProxy(engine); - } - } - // - } - try { - let rules: any; - // Force SSL off - rules = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) { - if (!transactionId) transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - } - } +// if (!containerRunning) { +// try { +// await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); +// console.log('removing', domain); +// transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { +// searchParams: { +// transaction_id: transactionId +// } +// }) +// .json(); +// } catch (error) { +// if (error?.response?.body) { +// const json = JSON.parse(error.response.body); +// if (json.code === 400 && json.message.includes('could not resolve address')) { +// await stopCoolifyProxy(engine); +// await startCoolifyProxy(engine); +// } +// } +// // +// } +// try { +// let rules: any; +// // Force SSL off +// rules = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => +// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) +// ); +// if (rule) { +// if (!transactionId) transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// } +// } - // Force WWW off - rules = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); - if (rule) { - if (!transactionId) transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - } - } - } catch (error) { - console.log(error); - // - } finally { - try { - if (transactionId) return transactionId; - } catch (error) { - if (error?.response?.body) { - const json = JSON.parse(error.response.body); - if (json.code === 400 && json.message.includes('could not resolve address')) { - await stopCoolifyProxy(engine); - await startCoolifyProxy(engine); - } - } - } - } - return; - } else { - let serverConfigured = false; - let backendAvailable: any = null; - try { - backendAvailable = await haproxy - .get(`v2/services/haproxy/configuration/backends/${domain}`) - .json(); - const server: any = await haproxy - .get(`v2/services/haproxy/configuration/servers/${id}`, { - searchParams: { - backend: domain - } - }) - .json(); +// // Force WWW off +// rules = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); +// if (rule) { +// if (!transactionId) transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// } +// } +// } catch (error) { +// console.log(error); +// // +// } finally { +// try { +// if (transactionId) return transactionId; +// } catch (error) { +// if (error?.response?.body) { +// const json = JSON.parse(error.response.body); +// if (json.code === 400 && json.message.includes('could not resolve address')) { +// await stopCoolifyProxy(engine); +// await startCoolifyProxy(engine); +// } +// } +// } +// } +// return; +// } else { +// let serverConfigured = false; +// let backendAvailable: any = null; +// try { +// backendAvailable = await haproxy +// .get(`v2/services/haproxy/configuration/backends/${domain}`) +// .json(); +// const server: any = await haproxy +// .get(`v2/services/haproxy/configuration/servers/${id}`, { +// searchParams: { +// backend: domain +// } +// }) +// .json(); - 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 === 'disabled') { - if (server.data.address === id) { - if (server.data.port === port) { - serverConfigured = true; - } - } - } - } - } - } - } catch (error) { - // - console.log(error); - } - if (serverConfigured) { - console.log('server configured', domain); - return; - } +// 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 === 'disabled') { +// if (server.data.address === id) { +// if (server.data.port === port) { +// serverConfigured = true; +// } +// } +// } +// } +// } +// } +// } catch (error) { +// // +// console.log(error); +// } +// if (serverConfigured) { +// console.log('server configured', domain); +// return; +// } - if (backendAvailable) { - if (!transactionId) transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - } - try { - console.log('adding ', domain); - if (!transactionId) 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: 'disabled', - name: id, - port: port - } - }); - let rules: any; +// if (backendAvailable) { +// if (!transactionId) transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { +// searchParams: { +// transaction_id: transactionId +// } +// }) +// .json(); +// } +// try { +// console.log('adding ', domain); +// if (!transactionId) 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: 'disabled', +// name: id, +// port: port +// } +// }); +// let rules: any; - // Force SSL off - rules = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) { - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - } - } - // Generate SSL && force SSL on - if (isHttps) { - await letsEncrypt(domain, id, false); - rules = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - let nextRule = 0; - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) return; - nextRule = rules.data[rules.data.length - 1].index + 1; - } +// // Force SSL off +// rules = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => +// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) +// ); +// if (rule) { +// await haproxy +// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// } +// } +// // Generate SSL && force SSL on +// if (isHttps) { +// await letsEncrypt(domain, id, false); +// rules = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// let nextRule = 0; +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => +// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) +// ); +// if (rule) return; +// nextRule = rules.data[rules.data.length - 1].index + 1; +// } - await haproxy - .post(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - }, - json: { - index: nextRule, - cond: 'if', - cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, - type: 'redirect', - redir_type: 'scheme', - redir_value: 'https', - redir_code: dev ? 302 : 301 - } - }) - .json(); - } +// await haproxy +// .post(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// }, +// json: { +// index: nextRule, +// cond: 'if', +// cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, +// type: 'redirect', +// redir_type: 'scheme', +// redir_value: 'https', +// redir_code: dev ? 302 : 301 +// } +// }) +// .json(); +// } - // WWW redirect on - rules = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - let nextRule = 0; - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); - if (rule) return; - nextRule = rules.data[rules.data.length - 1].index + 1; - } +// // WWW redirect on +// rules = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// let nextRule = 0; +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); +// if (rule) return; +// nextRule = rules.data[rules.data.length - 1].index + 1; +// } - await haproxy - .post(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - }, - json: { - index: nextRule, - cond: 'if', - cond_test: contTest, - type: 'redirect', - redir_type: 'location', - redir_value: redirectValue, - redir_code: dev ? 302 : 301 - } - }) - .json(); - } catch (error) { - console.log(error); - } finally { - try { - if (transactionId) return transactionId; - } catch (error) { - if (error?.response?.body) { - const json = JSON.parse(error.response.body); - if (json.code === 400 && json.message.includes('could not resolve address')) { - await stopCoolifyProxy(engine); - await startCoolifyProxy(engine); - } - } - } - } - } -} +// await haproxy +// .post(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// }, +// json: { +// index: nextRule, +// cond: 'if', +// cond_test: contTest, +// type: 'redirect', +// redir_type: 'location', +// redir_value: redirectValue, +// redir_code: dev ? 302 : 301 +// } +// }) +// .json(); +// } catch (error) { +// console.log(error); +// } finally { +// try { +// if (transactionId) return transactionId; +// } catch (error) { +// if (error?.response?.body) { +// const json = JSON.parse(error.response.body); +// if (json.code === 400 && json.message.includes('could not resolve address')) { +// await stopCoolifyProxy(engine); +// await startCoolifyProxy(engine); +// } +// } +// } +// } +// } +// } export async function configureCoolifyProxyOff(fqdn) { const domain = getDomain(fqdn); diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index a72cf6cb3..65949abf0 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -1,109 +1,104 @@ -import * as db from '$lib/database'; -import { getDomain } from '$lib/common'; -import { - checkContainer, - checkHAProxy, - checkProxyConfigurations, - configureCoolifyProxyOn, - configureHAProxy, - forceSSLOnApplication, - haproxyInstance, - setWwwRedirection, - startCoolifyProxy, - startHttpProxy -} from '$lib/haproxy'; +import { configureHAProxy } from '$lib/haproxy/configuration'; export default async function () { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let transactionId; try { - await checkProxyConfigurations(); + return await configureHAProxy(); } catch (error) { - console.log(error); + console.log(error.response.body || error); } - try { - const applications = await db.prisma.application.findMany({ - include: { destinationDocker: true } - }); + // const haproxy = await haproxyInstance(); + // await checkHAProxy(haproxy); + // const transactionId = await getNextTransactionId(); + // let executeTransaction = { + // applications: false, + // services: false + // } + // try { + // await checkProxyConfigurations(); + // } catch (error) { + // console.log(error); + // } + // try { + // const applications = await db.prisma.application.findMany({ + // include: { destinationDocker: true } + // }); - for (const application of applications) { - const { - fqdn, - id, - port, - destinationDocker: { engine } - } = application; - const containerRunning = await checkContainer(engine, id); - transactionId = await configureHAProxy( - haproxy, - transactionId, - fqdn, - id, - port, - containerRunning, - engine - ); - } + // for (const application of applications) { + // const { + // fqdn, + // id, + // port, + // destinationDocker: { engine } + // } = application; + // const containerRunning = await checkContainer(engine, id); + // executeTransaction.applications = await configureHAProxy( + // haproxy, + // transactionId, + // fqdn, + // id, + // port, + // containerRunning, + // engine + // ); + // } - const services = await db.prisma.service.findMany({ - include: { - destinationDocker: true, - minio: true, - plausibleAnalytics: true, - vscodeserver: true, - wordpress: true - } - }); + // const services = await db.prisma.service.findMany({ + // include: { + // destinationDocker: true, + // minio: true, + // plausibleAnalytics: true, + // vscodeserver: true, + // wordpress: true + // } + // }); - for (const service of services) { - const { - fqdn, - id, - type, - destinationDocker: { engine } - } = service; - console.log({ fqdn, id, type, engine }); - const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); - if (found) { - console.log(found); - const port = found.ports.main; - const publicPort = service[type]?.publicPort; - const containerRunning = await checkContainer(engine, id); - console.log(containerRunning); - transactionId = await configureHAProxy( - haproxy, - transactionId, - fqdn, - id, - port, - containerRunning, - engine - ); - if (publicPort) { - const containerFound = await checkContainer( - service.destinationDocker.engine, - `haproxy-for-${publicPort}` - ); - if (!containerFound) { - await startHttpProxy(service.destinationDocker, id, publicPort, 9000); - } - } - } - } - console.log(transactionId); - if (transactionId) await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); - // Check Coolify FQDN and configure proxy if needed - // const { fqdn } = await db.listSettings(); - // if (fqdn) { - // const domain = getDomain(fqdn); - // await startCoolifyProxy('/var/run/docker.sock'); - // await configureCoolifyProxyOn(fqdn); - // await setWwwRedirection(fqdn); - // const isHttps = fqdn.startsWith('https://'); - // if (isHttps) await forceSSLOnApplication(domain); - // } - } catch (error) { - throw error; - } + // for (const service of services) { + // const { + // fqdn, + // id, + // type, + // destinationDocker: { engine } + // } = service; + // const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + // if (found) { + // console.log(found); + // const port = found.ports.main; + // const publicPort = service[type]?.publicPort; + // const containerRunning = await checkContainer(engine, id); + // executeTransaction.services = await configureHAProxy( + // haproxy, + // transactionId, + // fqdn, + // id, + // port, + // containerRunning, + // engine + // ); + // if (publicPort) { + // const containerFound = await checkContainer( + // service.destinationDocker.engine, + // `haproxy-for-${publicPort}` + // ); + // if (!containerFound) { + // await startHttpProxy(service.destinationDocker, id, publicPort, 9000); + // } + // } + // } + // } + // if (executeTransaction.applications || executeTransaction.services) { + // await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); + // } + // // Check Coolify FQDN and configure proxy if needed + // // const { fqdn } = await db.listSettings(); + // // if (fqdn) { + // // const domain = getDomain(fqdn); + // // await startCoolifyProxy('/var/run/docker.sock'); + // // await configureCoolifyProxyOn(fqdn); + // // await setWwwRedirection(fqdn); + // // const isHttps = fqdn.startsWith('https://'); + // // if (isHttps) await forceSSLOnApplication(domain); + // // } + // } catch (error) { + // throw error; + // } } From 0325343edee7a39176bbf133abee06e9bb4e1d22 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 00:12:54 +0100 Subject: [PATCH 05/20] save --- src/lib/haproxy/configuration.ts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index 3792fc5b6..d12fdad77 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -65,6 +65,12 @@ backend {{domain}} option forwardfor server {{id}} {{id}}:{{port}} {{/applications}} +{{#services}} + +backend {{domain}} + option forwardfor + server {{id}} {{id}}:{{port}} +{{/services}} `; export async function haproxyInstance() { const { proxyPassword } = await db.listSettings(); @@ -108,10 +114,33 @@ export async function configureHAProxy() { }); } } + // const services = await db.prisma.service.findMany({ + // include: { + // destinationDocker: true, + // minio: true, + // plausibleAnalytics: true, + // vscodeserver: true, + // wordpress: true + // } + // }); + + // for (const service of services) { + // const { + // fqdn, + // id, + // type, + // destinationDocker: { engine } + // } = service; + // const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + // if (found) { + // const port = found.ports.main; + // const publicPort = service[type]?.publicPort; + // const isRunning = await checkContainer(engine, id); + // } + // } const output = mustache.render(template, data); const newHash = crypto.createHash('md5').update(JSON.stringify(template)).digest('hex'); const { proxyHash, id } = await db.listSettings(); - console.log(proxyHash, newHash); if (proxyHash !== newHash) { await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } }); console.log('HAProxy configuration changed, updating...'); From cb90f692f217e581716a885b607f97fded426067 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 00:20:28 +0100 Subject: [PATCH 06/20] WIP proxy --- src/lib/haproxy/configuration.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index d12fdad77..76a2623f4 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -60,10 +60,11 @@ backend backend-certbot server certbot host.docker.internal:9080 {{#applications}} - +{{#isRunning}} backend {{domain}} option forwardfor server {{id}} {{id}}:{{port}} +{{/isRunning}} {{/applications}} {{#services}} @@ -108,6 +109,7 @@ export async function configureHAProxy() { id, port, domain, + isRunning, isHttps, redirectValue, redirectTo: isWWW ? domain : 'www.' + domain @@ -139,7 +141,7 @@ export async function configureHAProxy() { // } // } const output = mustache.render(template, data); - const newHash = crypto.createHash('md5').update(JSON.stringify(template)).digest('hex'); + const newHash = crypto.createHash('md5').update(output).digest('hex'); const { proxyHash, id } = await db.listSettings(); if (proxyHash !== newHash) { await db.prisma.setting.update({ where: { id }, data: { proxyHash: newHash } }); From 823fe2deb22104624ba85a3b24cc3cb45cb310f7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 11:10:10 +0100 Subject: [PATCH 07/20] refactor --- package.json | 2 +- src/lib/haproxy/configuration.ts | 183 ++- src/lib/haproxy/index.ts | 1061 ++++++----------- src/lib/letsencrypt.ts | 2 +- src/lib/queues/builder.ts | 21 - src/lib/queues/cleanup.ts | 79 +- src/lib/queues/proxy.ts | 98 +- .../applications/[id]/previews/index.svelte | 32 +- src/routes/applications/[id]/stop.json.ts | 13 +- src/routes/destinations/[id]/restart.json.ts | 17 +- src/routes/services/[id]/minio/start.json.ts | 22 +- src/routes/services/[id]/minio/stop.json.ts | 8 +- src/routes/services/[id]/nocodb/start.json.ts | 24 +- src/routes/services/[id]/nocodb/stop.json.ts | 10 +- .../[id]/plausibleanalytics/start.json.ts | 21 - .../[id]/plausibleanalytics/stop.json.ts | 11 +- .../services/[id]/vaultwarden/start.json.ts | 23 +- .../services/[id]/vaultwarden/stop.json.ts | 11 +- .../services/[id]/vscodeserver/start.json.ts | 22 - .../services/[id]/vscodeserver/stop.json.ts | 10 +- .../services/[id]/wordpress/start.json.ts | 21 - .../services/[id]/wordpress/stop.json.ts | 10 +- src/routes/settings/index.json.ts | 58 +- 23 files changed, 603 insertions(+), 1156 deletions(-) diff --git a/package.json b/package.json index ac087f1bc..dd531e946 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.23", + "version": "2.0.24", "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", diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index 76a2623f4..26b76d3fd 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -5,12 +5,12 @@ import crypto from 'crypto'; import * as db from '$lib/database'; import { checkContainer, checkHAProxy } from '.'; -import { getDomain } from '$lib/common'; +import { asyncExecShell, getDomain, getEngine } from '$lib/common'; +import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; -let template = `#coolhash={{hash}} -program api +let template = `program api command /usr/bin/dataplaneapi -f /usr/local/etc/haproxy/dataplaneapi.hcl --userlist haproxy-dataplaneapi no option start-on-reload @@ -44,6 +44,26 @@ frontend http dev ? 302 : 301 } if { req.hdr(host) -i {{redirectTo}} } {{/applications}} + {{#services}} + {{#isHttps}} + http-request redirect scheme https code ${ + dev ? 302 : 301 + } if { hdr(host) -i {{domain}} } !{ ssl_fc } + {{/isHttps}} + http-request redirect location {{{redirectValue}}} code ${ + dev ? 302 : 301 + } if { req.hdr(host) -i {{redirectTo}} } + {{/services}} + {{#coolify}} + {{#isHttps}} + http-request redirect scheme https code ${ + dev ? 302 : 301 + } if { hdr(host) -i {{domain}} } !{ ssl_fc } + {{/isHttps}} + http-request redirect location {{{redirectValue}}} code ${ + dev ? 302 : 301 + } if { req.hdr(host) -i {{redirectTo}} } + {{/coolify}} use_backend backend-certbot if is_certbot use_backend %[req.hdr(host),lower] @@ -61,17 +81,28 @@ backend backend-certbot {{#applications}} {{#isRunning}} -backend {{domain}} - option forwardfor - server {{id}} {{id}}:{{port}} -{{/isRunning}} -{{/applications}} -{{#services}} backend {{domain}} option forwardfor - server {{id}} {{id}}:{{port}} + server {{id}} {{id}}:{{port}} check +{{/isRunning}} +{{/applications}} + +{{#services}} +{{#isRunning}} + +backend {{domain}} + option forwardfor + server {{id}} {{id}}:{{port}} check +{{/isRunning}} {{/services}} + +{{#coolify}} +backend {{domain}} + option forwardfor + option httpchk GET /undead.json + server {{id}} {{id}}:{{port}} check fall 10 +{{/coolify}} `; export async function haproxyInstance() { const { proxyPassword } = await db.listSettings(); @@ -85,26 +116,29 @@ export async function haproxyInstance() { export async function configureHAProxy() { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); + const ssls = []; const data = { applications: [], - services: [] + services: [], + coolify: [] }; const applications = await db.prisma.application.findMany({ - include: { destinationDocker: true } + include: { destinationDocker: true, settings: true } }); for (const application of applications) { const { fqdn, id, port, - destinationDocker: { engine } + destinationDocker: { engine, network }, + settings: { previews } } = application; const isRunning = await checkContainer(engine, id); + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; if (isRunning) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; data.applications.push({ id, port, @@ -114,32 +148,90 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? domain : 'www.' + domain }); + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + if (previews) { + const host = getEngine(engine); + const { stdout } = await asyncExecShell( + `DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` + ); + const containers = stdout + .trim() + .split('\n') + .filter((a) => a) + .map((c) => c.replace(/"/g, '')); + if (containers.length > 0) { + for (const container of containers) { + let previewDomain = `${container.split('-')[1]}.${domain}`; + data.applications.push({ + id: container, + port, + domain: previewDomain, + isRunning, + isHttps, + redirectValue, + redirectTo: isWWW ? previewDomain : 'www.' + previewDomain + }); + if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); + } + } } } - // const services = await db.prisma.service.findMany({ - // include: { - // destinationDocker: true, - // minio: true, - // plausibleAnalytics: true, - // vscodeserver: true, - // wordpress: true - // } - // }); + const services = await db.prisma.service.findMany({ + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); - // for (const service of services) { - // const { - // fqdn, - // id, - // type, - // destinationDocker: { engine } - // } = service; - // const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); - // if (found) { - // const port = found.ports.main; - // const publicPort = service[type]?.publicPort; - // const isRunning = await checkContainer(engine, id); - // } - // } + for (const service of services) { + const { + fqdn, + id, + type, + destinationDocker: { engine } + } = service; + const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + if (found) { + const port = found.ports.main; + const publicPort = service[type]?.publicPort; + const isRunning = await checkContainer(engine, id); + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + data.services.push({ + id, + port, + publicPort, + domain, + isRunning, + isHttps, + redirectValue, + redirectTo: isWWW ? domain : 'www.' + domain + }); + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + } + const { fqdn } = await db.prisma.setting.findFirst(); + if (fqdn) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + data.coolify.push({ + id: dev ? 'host.docker.internal' : 'coolify', + port: 3000, + domain, + isHttps, + redirectValue, + redirectTo: isWWW ? domain : 'www.' + domain + }); + if (!dev && isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true }); + } const output = mustache.render(template, data); const newHash = crypto.createHash('md5').update(output).digest('hex'); const { proxyHash, id } = await db.listSettings(); @@ -156,6 +248,15 @@ export async function configureHAProxy() { } }); } else { - console.log('HAProxy configuration is up to date'); + // console.log('HAProxy configuration is up to date'); + } + if (ssls.length > 0) { + for (const ssl of ssls) { + if (!dev) { + await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); + } else { + // console.log('Generate ssl for', ssl.domain); + } + } } } diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 734c3a038..f47c8c560 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -1,8 +1,7 @@ import { dev } from '$app/env'; -import { asyncExecShell, getDomain, getEngine } from '$lib/common'; +import { asyncExecShell, getEngine } from '$lib/common'; import got from 'got'; import * as db from '$lib/database'; -import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; @@ -49,112 +48,112 @@ export async function completeTransaction(transactionId) { return await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); } -export async function removeProxyConfiguration(fqdn) { - const domain = getDomain(fqdn); - const haproxy = await haproxyInstance(); - const backendFound = await haproxy - .get(`v2/services/haproxy/configuration/backends/${domain}`) - .json(); - if (backendFound) { - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - await completeTransaction(transactionId); - } - await forceSSLOffApplication(domain); - await removeWwwRedirection(fqdn); -} -export async function forceSSLOffApplication(domain) { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); +// export async function removeProxyConfiguration(fqdn) { +// const domain = getDomain(fqdn); +// const haproxy = await haproxyInstance(); +// const backendFound = await haproxy +// .get(`v2/services/haproxy/configuration/backends/${domain}`) +// .json(); +// if (backendFound) { +// const transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { +// searchParams: { +// transaction_id: transactionId +// } +// }) +// .json(); +// await completeTransaction(transactionId); +// } +// await forceSSLOffApplication(domain); +// await removeWwwRedirection(fqdn); +// } +// export async function forceSSLOffApplication(domain) { +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); - let transactionId; +// let transactionId; - try { - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) { - transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - } - } - } catch (error) { - console.log(error); - } finally { - if (transactionId) await completeTransaction(transactionId); - } -} -export async function forceSSLOnApplication(domain) { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let transactionId; - try { - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - let nextRule = 0; - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => - rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) - ); - if (rule) return; - nextRule = rules.data[rules.data.length - 1].index + 1; - } - transactionId = await getNextTransactionId(); +// try { +// const rules: any = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => +// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) +// ); +// if (rule) { +// transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// } +// } +// } catch (error) { +// console.log(error); +// } finally { +// if (transactionId) await completeTransaction(transactionId); +// } +// } +// export async function forceSSLOnApplication(domain) { +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// let transactionId; +// try { +// const rules: any = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// let nextRule = 0; +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => +// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) +// ); +// 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: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - }, - json: { - index: nextRule, - cond: 'if', - cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, - type: 'redirect', - redir_type: 'scheme', - redir_value: 'https', - redir_code: dev ? 302 : 301 - } - }) - .json(); - } catch (error) { - console.log(error); - throw error; - } finally { - if (transactionId) await completeTransaction(transactionId); - } -} +// await haproxy +// .post(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// }, +// json: { +// index: nextRule, +// cond: 'if', +// cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, +// type: 'redirect', +// redir_type: 'scheme', +// redir_value: 'https', +// redir_code: dev ? 302 : 301 +// } +// }) +// .json(); +// } catch (error) { +// console.log(error); +// throw error; +// } finally { +// if (transactionId) await completeTransaction(transactionId); +// } +// } export async function deleteProxy({ id }) { const haproxy = await haproxyInstance(); @@ -189,355 +188,55 @@ export async function reloadHaproxy(engine) { const host = getEngine(engine); return await asyncExecShell(`DOCKER_HOST=${host} docker exec coolify-haproxy kill -HUP 1`); } -export async function checkProxyConfigurations() { - const timeout = 10; - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - try { - const stats: any = await haproxy.get(`v2/services/haproxy/stats/native`).json(); - let transactionId = null; - for (const stat of stats[0].stats) { - if (stat.stats.status !== 'no check' && stat.type === 'server') { - if (!transactionId) await getNextTransactionId(); - const { backend_name: backendName } = stat; - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - } - } - if (transactionId) await completeTransaction(transactionId); - } catch (error) { - console.log(error.response.body); - } -} -// export async function configureHAProxy( -// haproxy, -// transactionId, -// fqdn, -// id, -// port, -// containerRunning, -// engine -// ) { -// const domain = getDomain(fqdn); -// const isHttps = fqdn.startsWith('https://'); -// const isWWW = fqdn.includes('www.'); -// const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; -// const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; - -// // console.log({ fqdn, domain, id, port, containerRunning, isHttps, isWWW }); - -// if (!containerRunning) { -// try { -// await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); -// console.log('removing', domain); -// transactionId = await getNextTransactionId(); -// await haproxy -// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { -// searchParams: { -// transaction_id: transactionId -// } -// }) -// .json(); -// } catch (error) { -// if (error?.response?.body) { -// const json = JSON.parse(error.response.body); -// if (json.code === 400 && json.message.includes('could not resolve address')) { -// await stopCoolifyProxy(engine); -// await startCoolifyProxy(engine); -// } -// } -// // -// } -// try { -// let rules: any; -// // Force SSL off -// rules = await haproxy -// .get(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// if (rules.data.length > 0) { -// const rule = rules.data.find((rule) => -// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) -// ); -// if (rule) { -// if (!transactionId) transactionId = await getNextTransactionId(); -// await haproxy -// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { -// searchParams: { -// transaction_id: transactionId, -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// } -// } - -// // Force WWW off -// rules = await haproxy -// .get(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// if (rules.data.length > 0) { -// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); -// if (rule) { -// if (!transactionId) transactionId = await getNextTransactionId(); -// await haproxy -// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { -// searchParams: { -// transaction_id: transactionId, -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// } -// } -// } catch (error) { -// console.log(error); -// // -// } finally { -// try { -// if (transactionId) return transactionId; -// } catch (error) { -// if (error?.response?.body) { -// const json = JSON.parse(error.response.body); -// if (json.code === 400 && json.message.includes('could not resolve address')) { -// await stopCoolifyProxy(engine); -// await startCoolifyProxy(engine); -// } -// } -// } -// } -// return; -// } else { -// let serverConfigured = false; -// let backendAvailable: any = null; -// try { -// backendAvailable = await haproxy -// .get(`v2/services/haproxy/configuration/backends/${domain}`) -// .json(); -// const server: any = await haproxy -// .get(`v2/services/haproxy/configuration/servers/${id}`, { -// searchParams: { -// backend: domain -// } -// }) -// .json(); - -// 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 === 'disabled') { -// if (server.data.address === id) { -// if (server.data.port === port) { -// serverConfigured = true; -// } -// } -// } -// } -// } -// } -// } catch (error) { -// // -// console.log(error); -// } -// if (serverConfigured) { -// console.log('server configured', domain); -// return; -// } - -// if (backendAvailable) { -// if (!transactionId) transactionId = await getNextTransactionId(); -// await haproxy -// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { -// searchParams: { -// transaction_id: transactionId -// } -// }) -// .json(); -// } -// try { -// console.log('adding ', domain); -// if (!transactionId) 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: 'disabled', -// name: id, -// port: port -// } -// }); -// let rules: any; - -// // Force SSL off -// rules = await haproxy -// .get(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// if (rules.data.length > 0) { -// const rule = rules.data.find((rule) => -// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) -// ); -// if (rule) { -// await haproxy -// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { -// searchParams: { -// transaction_id: transactionId, -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// } -// } -// // Generate SSL && force SSL on -// if (isHttps) { -// await letsEncrypt(domain, id, false); -// rules = await haproxy -// .get(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// let nextRule = 0; -// if (rules.data.length > 0) { -// const rule = rules.data.find((rule) => -// rule.cond_test.includes(`{ hdr(host) -i ${domain} } !{ ssl_fc }`) -// ); -// if (rule) return; -// nextRule = rules.data[rules.data.length - 1].index + 1; -// } - +// export async function checkProxyConfigurations() { +// const timeout = 10; +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// try { +// const stats: any = await haproxy.get(`v2/services/haproxy/stats/native`).json(); +// let transactionId = null; +// for (const stat of stats[0].stats) { +// if (stat.stats.status !== 'no check' && stat.type === 'server') { +// if (!transactionId) await getNextTransactionId(); +// const { backend_name: backendName } = stat; // await haproxy -// .post(`v2/services/haproxy/configuration/http_request_rules`, { +// .delete(`v2/services/haproxy/configuration/backends/${backendName}`, { // searchParams: { -// transaction_id: transactionId, -// parent_name: 'http', -// parent_type: 'frontend' -// }, -// json: { -// index: nextRule, -// cond: 'if', -// cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, -// type: 'redirect', -// redir_type: 'scheme', -// redir_value: 'https', -// redir_code: dev ? 302 : 301 +// transaction_id: transactionId // } // }) // .json(); // } - -// // WWW redirect on -// rules = await haproxy -// .get(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// parent_name: 'http', -// parent_type: 'frontend' -// } -// }) -// .json(); -// let nextRule = 0; -// if (rules.data.length > 0) { -// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); -// if (rule) return; -// nextRule = rules.data[rules.data.length - 1].index + 1; -// } - -// await haproxy -// .post(`v2/services/haproxy/configuration/http_request_rules`, { -// searchParams: { -// transaction_id: transactionId, -// parent_name: 'http', -// parent_type: 'frontend' -// }, -// json: { -// index: nextRule, -// cond: 'if', -// cond_test: contTest, -// type: 'redirect', -// redir_type: 'location', -// redir_value: redirectValue, -// redir_code: dev ? 302 : 301 -// } -// }) -// .json(); -// } catch (error) { -// console.log(error); -// } finally { -// try { -// if (transactionId) return transactionId; -// } catch (error) { -// if (error?.response?.body) { -// const json = JSON.parse(error.response.body); -// if (json.code === 400 && json.message.includes('could not resolve address')) { -// await stopCoolifyProxy(engine); -// await startCoolifyProxy(engine); -// } -// } -// } // } +// if (transactionId) await completeTransaction(transactionId); +// } catch (error) { +// console.log(error.response.body); // } // } -export async function configureCoolifyProxyOff(fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); +// export async function configureCoolifyProxyOff(fqdn) { +// const domain = getDomain(fqdn); +// const isHttps = fqdn.startsWith('https://'); +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); - try { - await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - await completeTransaction(transactionId); - if (isHttps) await forceSSLOffApplication(domain); - await removeWwwRedirection(fqdn); - } catch (error) { - throw error?.response?.body || error; - } -} +// try { +// await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); +// const transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { +// searchParams: { +// transaction_id: transactionId +// } +// }) +// .json(); +// await completeTransaction(transactionId); +// if (isHttps) await forceSSLOffApplication(domain); +// await removeWwwRedirection(fqdn); +// } catch (error) { +// throw error?.response?.body || error; +// } +// } export async function checkHAProxy(haproxy?: any) { if (!haproxy) haproxy = await haproxyInstance(); try { @@ -549,76 +248,76 @@ export async function checkHAProxy(haproxy?: any) { }; } } -export async function configureCoolifyProxyOn(fqdn) { - const domain = getDomain(fqdn); - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let serverConfigured = false; - let backendAvailable: any = null; - try { - backendAvailable = await haproxy - .get(`v2/services/haproxy/configuration/backends/${domain}`) - .json(); - const server: any = await haproxy - .get(`v2/services/haproxy/configuration/servers/coolify`, { - searchParams: { - backend: domain - } - }) - .json(); - 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 === dev ? 'host.docker.internal' : 'coolify') { - if (server.data.port === 3000) { - serverConfigured = true; - } - } - } - } - } - } - } catch (error) {} - if (serverConfigured) return; - const transactionId = await getNextTransactionId(); - try { - await haproxy.post('v2/services/haproxy/configuration/backends', { - searchParams: { - transaction_id: transactionId - }, - json: { - adv_check: 'httpchk', - httpchk_params: { - method: 'GET', - uri: '/undead.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: dev ? 'host.docker.internal' : 'coolify', - check: 'enable', - fall: 10, - name: 'coolify', - port: 3000 - } - }); - } catch (error) { - console.log(error); - throw error; - } finally { - await completeTransaction(transactionId); - } -} +// export async function configureCoolifyProxyOn(fqdn) { +// const domain = getDomain(fqdn); +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// let serverConfigured = false; +// let backendAvailable: any = null; +// try { +// backendAvailable = await haproxy +// .get(`v2/services/haproxy/configuration/backends/${domain}`) +// .json(); +// const server: any = await haproxy +// .get(`v2/services/haproxy/configuration/servers/coolify`, { +// searchParams: { +// backend: domain +// } +// }) +// .json(); +// 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 === dev ? 'host.docker.internal' : 'coolify') { +// if (server.data.port === 3000) { +// serverConfigured = true; +// } +// } +// } +// } +// } +// } +// } catch (error) {} +// if (serverConfigured) return; +// const transactionId = await getNextTransactionId(); +// try { +// await haproxy.post('v2/services/haproxy/configuration/backends', { +// searchParams: { +// transaction_id: transactionId +// }, +// json: { +// adv_check: 'httpchk', +// httpchk_params: { +// method: 'GET', +// uri: '/undead.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: dev ? 'host.docker.internal' : 'coolify', +// check: 'enable', +// fall: 10, +// name: 'coolify', +// port: 3000 +// } +// }); +// } catch (error) { +// console.log(error); +// throw error; +// } finally { +// await completeTransaction(transactionId); +// } +// } export async function stopTcpHttpProxy(destinationDocker, publicPort) { const { engine } = destinationDocker; @@ -748,179 +447,179 @@ export async function configureNetworkCoolifyProxy(engine) { }); } -export async function configureSimpleServiceProxyOn({ id, domain, port }) { - console.log({ service: true, id, domain, port }); - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let serverConfigured = false; - let backendAvailable: any = null; +// export async function configureSimpleServiceProxyOn({ id, domain, port }) { +// console.log({ service: true, id, domain, port }); +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// let serverConfigured = false; +// let backendAvailable: any = null; - try { - backendAvailable = await haproxy - .get(`v2/services/haproxy/configuration/backends/${domain}`) - .json(); - const server: any = await haproxy - .get(`v2/services/haproxy/configuration/servers/${id}`, { - searchParams: { - backend: domain - } - }) - .json(); - 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) {} - 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: 'enable', - name: id, - port: port - } - }); - await completeTransaction(transactionId); -} +// try { +// backendAvailable = await haproxy +// .get(`v2/services/haproxy/configuration/backends/${domain}`) +// .json(); +// const server: any = await haproxy +// .get(`v2/services/haproxy/configuration/servers/${id}`, { +// searchParams: { +// backend: domain +// } +// }) +// .json(); +// 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) {} +// 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: 'enable', +// name: id, +// port: port +// } +// }); +// await completeTransaction(transactionId); +// } -export async function configureSimpleServiceProxyOff(fqdn) { - if (!fqdn) { - return; - } - const domain = getDomain(fqdn); - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - try { - await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); - const transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/backends/${domain}`, { - searchParams: { - transaction_id: transactionId - } - }) - .json(); - await completeTransaction(transactionId); - } catch (error) {} - await forceSSLOffApplication(domain); - await removeWwwRedirection(fqdn); - return; -} +// export async function configureSimpleServiceProxyOff(fqdn) { +// if (!fqdn) { +// return; +// } +// const domain = getDomain(fqdn); +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// try { +// await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); +// const transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/backends/${domain}`, { +// searchParams: { +// transaction_id: transactionId +// } +// }) +// .json(); +// await completeTransaction(transactionId); +// } catch (error) {} +// await forceSSLOffApplication(domain); +// await removeWwwRedirection(fqdn); +// return; +// } -export async function removeWwwRedirection(fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; +// export async function removeWwwRedirection(fqdn) { +// const domain = getDomain(fqdn); +// const isHttps = fqdn.startsWith('https://'); +// const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; - const haproxy = await haproxyInstance(); - await checkHAProxy(); +// const haproxy = await haproxyInstance(); +// await checkHAProxy(); - let transactionId; +// let transactionId; - try { - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); - if (rule) { - transactionId = await getNextTransactionId(); - await haproxy - .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { - searchParams: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - } - } - } catch (error) { - console.log(error); - } finally { - if (transactionId) await completeTransaction(transactionId); - } -} -export async function setWwwRedirection(fqdn) { - const haproxy = await haproxyInstance(); - await checkHAProxy(haproxy); - let transactionId; +// try { +// const rules: any = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); +// if (rule) { +// transactionId = await getNextTransactionId(); +// await haproxy +// .delete(`v2/services/haproxy/configuration/http_request_rules/${rule.index}`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// } +// } +// } catch (error) { +// console.log(error); +// } finally { +// if (transactionId) await completeTransaction(transactionId); +// } +// } +// export async function setWwwRedirection(fqdn) { +// const haproxy = await haproxyInstance(); +// await checkHAProxy(haproxy); +// let transactionId; - try { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; - const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; - const rules: any = await haproxy - .get(`v2/services/haproxy/configuration/http_request_rules`, { - searchParams: { - parent_name: 'http', - parent_type: 'frontend' - } - }) - .json(); - let nextRule = 0; - if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); - if (rule) return; - nextRule = rules.data[rules.data.length - 1].index + 1; - } +// try { +// const domain = getDomain(fqdn); +// const isHttps = fqdn.startsWith('https://'); +// const isWWW = fqdn.includes('www.'); +// const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; +// const contTest = `{ req.hdr(host) -i ${isWWW ? domain.replace('www.', '') : `www.${domain}`} }`; +// const rules: any = await haproxy +// .get(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// parent_name: 'http', +// parent_type: 'frontend' +// } +// }) +// .json(); +// let nextRule = 0; +// if (rules.data.length > 0) { +// const rule = rules.data.find((rule) => rule.redir_value.includes(redirectValue)); +// 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: { - transaction_id: transactionId, - parent_name: 'http', - parent_type: 'frontend' - }, - json: { - index: nextRule, - cond: 'if', - cond_test: contTest, - type: 'redirect', - redir_type: 'location', - redir_value: redirectValue, - redir_code: dev ? 302 : 301 - } - }) - .json(); - } catch (error) { - console.log(error); - throw error; - } finally { - if (transactionId) await completeTransaction(transactionId); - } -} +// transactionId = await getNextTransactionId(); +// await haproxy +// .post(`v2/services/haproxy/configuration/http_request_rules`, { +// searchParams: { +// transaction_id: transactionId, +// parent_name: 'http', +// parent_type: 'frontend' +// }, +// json: { +// index: nextRule, +// cond: 'if', +// cond_test: contTest, +// type: 'redirect', +// redir_type: 'location', +// redir_value: redirectValue, +// redir_code: dev ? 302 : 301 +// } +// }) +// .json(); +// } catch (error) { +// console.log(error); +// throw error; +// } finally { +// if (transactionId) await completeTransaction(transactionId); +// } +// } diff --git a/src/lib/letsencrypt.ts b/src/lib/letsencrypt.ts index c062287ea..b3846eba7 100644 --- a/src/lib/letsencrypt.ts +++ b/src/lib/letsencrypt.ts @@ -13,7 +13,7 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) { const nakedDomain = domain.replace('www.', ''); const wwwDomain = `www.${nakedDomain}`; const randomCuid = cuid(); - const randomPort = await getPort({ port: portNumbers(minPort, maxPort) }); + const randomPort = 9000; let host; let dualCerts = false; diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 0de934bef..54976b050 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -256,26 +256,5 @@ export default async function (job) { throw new Error(error); } saveBuildLog({ line: 'Proxy will be configured shortly.', buildId, applicationId }); - // try { - // if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { - // saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId }); - // await checkProxyConfigurations(); - // await configureProxyForApplication(domain, imageId, port); - // if (isHttps) await letsEncrypt({ domain, id: applicationId }); - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); - // saveBuildLog({ line: 'Proxy configuration successful!', buildId, applicationId }); - // } else { - // saveBuildLog({ - // line: 'Coolify Proxy is not configured for this destination. Nothing else to do.', - // buildId, - // applicationId - // }); - // } - // } catch (error) { - // saveBuildLog({ line: error.stdout || error, buildId, applicationId }); - // sentry.captureException(error); - // throw new Error(error); - // } } } diff --git a/src/lib/queues/cleanup.ts b/src/lib/queues/cleanup.ts index 981173a31..53c1896a7 100644 --- a/src/lib/queues/cleanup.ts +++ b/src/lib/queues/cleanup.ts @@ -8,48 +8,53 @@ export default async function () { for (const destinationDocker of destinationDockers) { const host = getEngine(destinationDocker.engine); // Tagging images with labels - try { - const images = [ - `coollabsio/${defaultProxyImageTcp}`, - `coollabsio/${defaultProxyImageHttp}`, - 'certbot/certbot:latest', - 'node:16.14.0-alpine', - 'alpine:latest', - 'nginx:stable-alpine', - 'node:lts', - 'php:apache', - 'rust:latest' - ]; - for (const image of images) { - try { - await asyncExecShell(`DOCKER_HOST=${host} docker image inspect ${image}`); - } catch (error) { - await asyncExecShell( - `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -` - ); - } - } - } catch (error) {} + // try { + // const images = [ + // `coollabsio/${defaultProxyImageTcp}`, + // `coollabsio/${defaultProxyImageHttp}`, + // 'certbot/certbot:latest', + // 'node:16.14.0-alpine', + // 'alpine:latest', + // 'nginx:stable-alpine', + // 'node:lts', + // 'php:apache', + // 'rust:latest' + // ]; + // for (const image of images) { + // try { + // await asyncExecShell(`DOCKER_HOST=${host} docker image inspect ${image}`); + // } catch (error) { + // await asyncExecShell( + // `DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.image="true" -t "${image}" -` + // ); + // } + // } + // } catch (error) {} try { await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`); } catch (error) { console.log(error); } - if (!dev) { - // Cleanup images that are not managed by coolify - try { - await asyncExecShell( - `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f` - ); - } catch (error) { - console.log(error); - } - // Cleanup old images >3 days - try { - await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`); - } catch (error) { - console.log(error); - } + try { + await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`); + } catch (error) { + console.log(error); } + // if (!dev) { + // // Cleanup images that are not managed by coolify + // try { + // await asyncExecShell( + // `DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.image=true' -a -f` + // ); + // } catch (error) { + // console.log(error); + // } + // // Cleanup old images >3 days + // try { + // await asyncExecShell(`DOCKER_HOST=${host} docker image prune --filter "until=72h" -a -f`); + // } catch (error) { + // console.log(error); + // } + // } } } diff --git a/src/lib/queues/proxy.ts b/src/lib/queues/proxy.ts index 65949abf0..3d5f05181 100644 --- a/src/lib/queues/proxy.ts +++ b/src/lib/queues/proxy.ts @@ -1,104 +1,10 @@ +import { ErrorHandler } from '$lib/database'; import { configureHAProxy } from '$lib/haproxy/configuration'; export default async function () { try { return await configureHAProxy(); } catch (error) { - console.log(error.response.body || error); + ErrorHandler(error.response.body || error); } - // const haproxy = await haproxyInstance(); - // await checkHAProxy(haproxy); - // const transactionId = await getNextTransactionId(); - // let executeTransaction = { - // applications: false, - // services: false - // } - // try { - // await checkProxyConfigurations(); - // } catch (error) { - // console.log(error); - // } - // try { - // const applications = await db.prisma.application.findMany({ - // include: { destinationDocker: true } - // }); - - // for (const application of applications) { - // const { - // fqdn, - // id, - // port, - // destinationDocker: { engine } - // } = application; - // const containerRunning = await checkContainer(engine, id); - // executeTransaction.applications = await configureHAProxy( - // haproxy, - // transactionId, - // fqdn, - // id, - // port, - // containerRunning, - // engine - // ); - // } - - // const services = await db.prisma.service.findMany({ - // include: { - // destinationDocker: true, - // minio: true, - // plausibleAnalytics: true, - // vscodeserver: true, - // wordpress: true - // } - // }); - - // for (const service of services) { - // const { - // fqdn, - // id, - // type, - // destinationDocker: { engine } - // } = service; - // const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); - // if (found) { - // console.log(found); - // const port = found.ports.main; - // const publicPort = service[type]?.publicPort; - // const containerRunning = await checkContainer(engine, id); - // executeTransaction.services = await configureHAProxy( - // haproxy, - // transactionId, - // fqdn, - // id, - // port, - // containerRunning, - // engine - // ); - // if (publicPort) { - // const containerFound = await checkContainer( - // service.destinationDocker.engine, - // `haproxy-for-${publicPort}` - // ); - // if (!containerFound) { - // await startHttpProxy(service.destinationDocker, id, publicPort, 9000); - // } - // } - // } - // } - // if (executeTransaction.applications || executeTransaction.services) { - // await haproxy.put(`v2/services/haproxy/transactions/${transactionId}`); - // } - // // Check Coolify FQDN and configure proxy if needed - // // const { fqdn } = await db.listSettings(); - // // if (fqdn) { - // // const domain = getDomain(fqdn); - // // await startCoolifyProxy('/var/run/docker.sock'); - // // await configureCoolifyProxyOn(fqdn); - // // await setWwwRedirection(fqdn); - // // const isHttps = fqdn.startsWith('https://'); - // // if (isHttps) await forceSSLOnApplication(domain); - // // } - // } catch (error) { - // throw error; - // } } diff --git a/src/routes/applications/[id]/previews/index.svelte b/src/routes/applications/[id]/previews/index.svelte index 59b1e4aa2..9ac2dd940 100644 --- a/src/routes/applications/[id]/previews/index.svelte +++ b/src/routes/applications/[id]/previews/index.svelte @@ -44,33 +44,19 @@
- - - - - - -
NameValueNeed during buildtime? + + + + + + + - + {#each applicationSecrets as secret} {#key secret.id} - + s.name === secret.name)} isPRMRSecret diff --git a/src/routes/applications/[id]/stop.json.ts b/src/routes/applications/[id]/stop.json.ts index 58064c07a..999d8c106 100644 --- a/src/routes/applications/[id]/stop.json.ts +++ b/src/routes/applications/[id]/stop.json.ts @@ -1,8 +1,7 @@ -import { getDomain, getUserDetails } from '$lib/common'; +import { asyncExecShell, getEngine, getUserDetails, removeDestinationDocker } from '$lib/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { removeProxyConfiguration } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -17,10 +16,12 @@ export const post: RequestHandler = async (event) => { teamId }); if (destinationDockerId) { - const docker = dockerInstance({ destinationDocker }); - await docker.engine.getContainer(id).stop(); + const { engine } = destinationDocker; + const found = await checkContainer(engine, id); + if (found) { + await removeDestinationDocker({ id, engine }); + } } - // await removeProxyConfiguration(fqdn); return { status: 200 }; diff --git a/src/routes/destinations/[id]/restart.json.ts b/src/routes/destinations/[id]/restart.json.ts index 65a3e0cd4..9d2da459a 100644 --- a/src/routes/destinations/[id]/restart.json.ts +++ b/src/routes/destinations/[id]/restart.json.ts @@ -1,30 +1,19 @@ -import { getDomain, getUserDetails } from '$lib/common'; +import { 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 { 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(); + const { engine } = 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 }; diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index 1d1a0655e..5512bded8 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -3,15 +3,7 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection, - startHttpProxy -} from '$lib/haproxy'; +import { startHttpProxy } from '$lib/haproxy'; import getPort, { portNumbers } from 'get-port'; import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; @@ -24,7 +16,6 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - // await checkHAProxy(); const service = await db.getService({ id, teamId }); const { type, @@ -38,9 +29,6 @@ export const post: RequestHandler = async (event) => { const data = await db.prisma.setting.findFirst(); const { minPort, maxPort } = data; - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); @@ -96,16 +84,8 @@ export const post: RequestHandler = async (event) => { } try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: consolePort }); await db.updateMinioService({ id, publicPort }); await startHttpProxy(destinationDocker, id, publicPort, apiPort); - - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/minio/stop.json.ts b/src/routes/services/[id]/minio/stop.json.ts index 0285e776c..3e2340237 100644 --- a/src/routes/services/[id]/minio/stop.json.ts +++ b/src/routes/services/[id]/minio/stop.json.ts @@ -1,9 +1,7 @@ -import { getEngine, getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; +import { getUserDetails, removeDestinationDocker } from '$lib/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff, stopTcpHttpProxy } from '$lib/haproxy'; +import { checkContainer, stopTcpHttpProxy } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -21,7 +19,6 @@ export const post: RequestHandler = async (event) => { minio: { publicPort } } = service; await db.updateMinioService({ id, publicPort: null }); - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; @@ -35,7 +32,6 @@ export const post: RequestHandler = async (event) => { } try { await stopTcpHttpProxy(destinationDocker, publicPort); - // await configureSimpleServiceProxyOff(fqdn); } catch (error) { console.log(error); } diff --git a/src/routes/services/[id]/nocodb/start.json.ts b/src/routes/services/[id]/nocodb/start.json.ts index 82ccd2a1c..ef9ac485c 100644 --- a/src/routes/services/[id]/nocodb/start.json.ts +++ b/src/routes/services/[id]/nocodb/start.json.ts @@ -3,15 +3,6 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection -} from '$lib/haproxy'; -import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; @@ -22,13 +13,8 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); const service = await db.getService({ id, teamId }); - const { type, version, fqdn, destinationDockerId, destinationDocker } = service; - - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - + const { type, version, destinationDockerId, destinationDocker } = service; const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); @@ -56,14 +42,6 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); - - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/nocodb/stop.json.ts b/src/routes/services/[id]/nocodb/stop.json.ts index f15ba1012..c604e1cc3 100644 --- a/src/routes/services/[id]/nocodb/stop.json.ts +++ b/src/routes/services/[id]/nocodb/stop.json.ts @@ -1,9 +1,7 @@ import { getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => { try { const service = await db.getService({ id, teamId }); const { destinationDockerId, destinationDocker, fqdn } = service; - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; @@ -27,11 +24,6 @@ export const post: RequestHandler = async (event) => { } catch (error) { console.error(error); } - try { - await configureSimpleServiceProxyOff(fqdn); - } catch (error) { - console.log(error); - } } return { diff --git a/src/routes/services/[id]/plausibleanalytics/start.json.ts b/src/routes/services/[id]/plausibleanalytics/start.json.ts index fe0171267..70de54499 100644 --- a/src/routes/services/[id]/plausibleanalytics/start.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/start.json.ts @@ -3,15 +3,6 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection -} from '$lib/haproxy'; -import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; @@ -22,7 +13,6 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); const service = await db.getService({ id, teamId }); const { type, @@ -42,9 +32,6 @@ export const post: RequestHandler = async (event) => { } } = service; - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const config = { plausibleAnalytics: { image: `plausible/analytics:${version}`, @@ -83,7 +70,6 @@ export const post: RequestHandler = async (event) => { }; const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); - const engine = destinationDocker.engine; const { workdir } = await createDirectories({ repository: type, buildId: id }); @@ -187,14 +173,7 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`; await asyncExecShell( `DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d` ); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: 8000 }); - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/plausibleanalytics/stop.json.ts b/src/routes/services/[id]/plausibleanalytics/stop.json.ts index d9b42d7b3..6ff3cac3b 100644 --- a/src/routes/services/[id]/plausibleanalytics/stop.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/stop.json.ts @@ -1,9 +1,7 @@ import { getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => { try { const service = await db.getService({ id, teamId }); const { destinationDockerId, destinationDocker, fqdn } = service; - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; @@ -36,12 +33,6 @@ export const post: RequestHandler = async (event) => { } catch (error) { console.error(error); } - - try { - await configureSimpleServiceProxyOff(fqdn); - } catch (error) { - console.log(error); - } } return { diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts index 5dcacbea3..f42481200 100644 --- a/src/routes/services/[id]/vaultwarden/start.json.ts +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -3,15 +3,6 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection -} from '$lib/haproxy'; -import { getDomain } from '$lib/components/common'; import { getServiceImage, ErrorHandler } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; @@ -22,12 +13,8 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); const service = await db.getService({ id, teamId }); - const { type, version, fqdn, destinationDockerId, destinationDocker } = service; - - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); + const { type, version, destinationDockerId, destinationDocker } = service; const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); @@ -74,14 +61,6 @@ export const post: RequestHandler = async (event) => { } try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: 80 }); - - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/vaultwarden/stop.json.ts b/src/routes/services/[id]/vaultwarden/stop.json.ts index f15ba1012..3c3bfbc62 100644 --- a/src/routes/services/[id]/vaultwarden/stop.json.ts +++ b/src/routes/services/[id]/vaultwarden/stop.json.ts @@ -1,9 +1,7 @@ import { getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => { try { const service = await db.getService({ id, teamId }); const { destinationDockerId, destinationDocker, fqdn } = service; - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; @@ -27,13 +24,7 @@ export const post: RequestHandler = async (event) => { } catch (error) { console.error(error); } - try { - await configureSimpleServiceProxyOff(fqdn); - } catch (error) { - console.log(error); - } } - return { status: 200 }; diff --git a/src/routes/services/[id]/vscodeserver/start.json.ts b/src/routes/services/[id]/vscodeserver/start.json.ts index ee5f2670b..29bf3326a 100644 --- a/src/routes/services/[id]/vscodeserver/start.json.ts +++ b/src/routes/services/[id]/vscodeserver/start.json.ts @@ -3,15 +3,6 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection -} from '$lib/haproxy'; -import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; @@ -22,20 +13,15 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); const service = await db.getService({ id, teamId }); const { type, version, - fqdn, destinationDockerId, destinationDocker, vscodeserver: { password } } = service; - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); @@ -84,14 +70,6 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: 8080 }); - - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/vscodeserver/stop.json.ts b/src/routes/services/[id]/vscodeserver/stop.json.ts index cf748b709..3c3bfbc62 100644 --- a/src/routes/services/[id]/vscodeserver/stop.json.ts +++ b/src/routes/services/[id]/vscodeserver/stop.json.ts @@ -1,9 +1,7 @@ import { getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => { try { const service = await db.getService({ id, teamId }); const { destinationDockerId, destinationDocker, fqdn } = service; - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; @@ -27,11 +24,6 @@ export const post: RequestHandler = async (event) => { } catch (error) { console.error(error); } - try { - await configureSimpleServiceProxyOff(fqdn); - } catch (error) { - console.log(error); - } } return { status: 200 diff --git a/src/routes/services/[id]/wordpress/start.json.ts b/src/routes/services/[id]/wordpress/start.json.ts index 76e113bce..54080478e 100644 --- a/src/routes/services/[id]/wordpress/start.json.ts +++ b/src/routes/services/[id]/wordpress/start.json.ts @@ -3,15 +3,6 @@ import * as db from '$lib/database'; import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; -import { letsEncrypt } from '$lib/letsencrypt'; -import { - checkHAProxy, - checkProxyConfigurations, - configureSimpleServiceProxyOn, - reloadHaproxy, - setWwwRedirection -} from '$lib/haproxy'; -import { getDomain } from '$lib/components/common'; import { ErrorHandler } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; @@ -22,7 +13,6 @@ export const post: RequestHandler = async (event) => { const { id } = event.params; try { - await checkHAProxy(); const service = await db.getService({ id, teamId }); const { type, @@ -40,9 +30,6 @@ export const post: RequestHandler = async (event) => { } } = service; - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); @@ -121,14 +108,6 @@ export const post: RequestHandler = async (event) => { try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - // await checkProxyConfigurations(); - // await configureSimpleServiceProxyOn({ id, domain, port: 80 }); - - // if (isHttps) { - // await letsEncrypt({ domain, id }); - // } - // await setWwwRedirection(fqdn); - // await reloadHaproxy(destinationDocker.engine); return { status: 200 }; diff --git a/src/routes/services/[id]/wordpress/stop.json.ts b/src/routes/services/[id]/wordpress/stop.json.ts index 800a62de3..2fc32b5a1 100644 --- a/src/routes/services/[id]/wordpress/stop.json.ts +++ b/src/routes/services/[id]/wordpress/stop.json.ts @@ -1,9 +1,7 @@ import { getUserDetails, removeDestinationDocker } from '$lib/common'; -import { getDomain } from '$lib/components/common'; import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { checkContainer, configureSimpleServiceProxyOff } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const post: RequestHandler = async (event) => { @@ -15,7 +13,6 @@ export const post: RequestHandler = async (event) => { try { const service = await db.getService({ id, teamId }); const { destinationDockerId, destinationDocker, fqdn } = service; - const domain = getDomain(fqdn); if (destinationDockerId) { const engine = destinationDocker.engine; try { @@ -30,11 +27,6 @@ export const post: RequestHandler = async (event) => { } catch (error) { console.error(error); } - try { - await configureSimpleServiceProxyOff(fqdn); - } catch (error) { - console.log(error); - } } return { diff --git a/src/routes/settings/index.json.ts b/src/routes/settings/index.json.ts index bb823ed58..163877fb0 100644 --- a/src/routes/settings/index.json.ts +++ b/src/routes/settings/index.json.ts @@ -1,18 +1,6 @@ -import { dev } from '$app/env'; -import { getDomain, getUserDetails } from '$lib/common'; +import { getUserDetails } from '$lib/common'; import * as db from '$lib/database'; import { listSettings, ErrorHandler } from '$lib/database'; -import { - checkProxyConfigurations, - configureCoolifyProxyOff, - configureCoolifyProxyOn, - forceSSLOnApplication, - reloadHaproxy, - removeWwwRedirection, - setWwwRedirection, - startCoolifyProxy -} from '$lib/haproxy'; -import { letsEncrypt } from '$lib/letsencrypt'; import type { RequestHandler } from '@sveltejs/kit'; import { promises as dns } from 'dns'; @@ -52,10 +40,7 @@ export const del: RequestHandler = async (event) => { // Do not care. } try { - const domain = getDomain(fqdn); await db.prisma.setting.update({ where: { fqdn }, data: { fqdn: null } }); - await configureCoolifyProxyOff(fqdn); - await removeWwwRedirection(domain); return { status: 200, body: { @@ -80,50 +65,19 @@ export const post: RequestHandler = async (event) => { const { fqdn, isRegistrationEnabled, dualCerts, minPort, maxPort } = await event.request.json(); try { - await checkProxyConfigurations(); - const { - id, - fqdn: oldFqdn, - isRegistrationEnabled: oldIsRegistrationEnabled, - dualCerts: oldDualCerts - } = await db.listSettings(); - if (oldIsRegistrationEnabled !== isRegistrationEnabled) { + const { id } = await db.listSettings(); + if (isRegistrationEnabled) { await db.prisma.setting.update({ where: { id }, data: { isRegistrationEnabled } }); } - if (oldDualCerts !== dualCerts) { - await db.prisma.setting.update({ where: { id }, data: { dualCerts } }); - } - if (oldFqdn && oldFqdn !== fqdn) { - if (oldFqdn) { - const oldDomain = getDomain(oldFqdn); - await configureCoolifyProxyOff(oldFqdn); - await removeWwwRedirection(oldDomain); - } - } if (fqdn) { - await startCoolifyProxy('/var/run/docker.sock'); - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (domain) { - await configureCoolifyProxyOn(fqdn); - await setWwwRedirection(fqdn); - if (isHttps) { - await letsEncrypt({ domain, isCoolify: true }); - await forceSSLOnApplication(domain); - await reloadHaproxy('/var/run/docker.sock'); - } - } - await db.prisma.setting.update({ where: { id }, data: { fqdn } }); - await db.prisma.destinationDocker.updateMany({ - where: { engine: '/var/run/docker.sock' }, - data: { isCoolifyProxyUsed: true } - }); + } + if (dualCerts) { + await db.prisma.setting.update({ where: { id }, data: { dualCerts } }); } if (minPort && maxPort) { await db.prisma.setting.update({ where: { id }, data: { minPort, maxPort } }); } - return { status: 201 }; From 76ba338b455a450da10fab47b24dd6dd3c945f09 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 11:17:34 +0100 Subject: [PATCH 08/20] fix --- src/lib/database/applications.ts | 1 - src/routes/webhooks/github/events.ts | 3 +-- src/routes/webhooks/gitlab/events.ts | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index b2d7ceacd..79ee38876 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -1,5 +1,4 @@ import { decrypt, encrypt } from '$lib/crypto'; -import { removeProxyConfiguration } from '$lib/haproxy'; import { asyncExecShell, getEngine } from '$lib/common'; import { getDomain, removeDestinationDocker } from '$lib/common'; diff --git a/src/routes/webhooks/github/events.ts b/src/routes/webhooks/github/events.ts index 1be335871..91ac4d301 100644 --- a/src/routes/webhooks/github/events.ts +++ b/src/routes/webhooks/github/events.ts @@ -4,7 +4,7 @@ import type { RequestHandler } from '@sveltejs/kit'; import cuid from 'cuid'; import crypto from 'crypto'; import { buildQueue } from '$lib/queues'; -import { checkContainer, removeProxyConfiguration } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import { dev } from '$app/env'; export const options: RequestHandler = async () => { @@ -154,7 +154,6 @@ export const post: RequestHandler = async (event) => { const engine = applicationFound.destinationDocker.engine; await removeDestinationDocker({ id, engine }); - await removeProxyConfiguration(fqdn); } return { status: 200, diff --git a/src/routes/webhooks/gitlab/events.ts b/src/routes/webhooks/gitlab/events.ts index b1af61fd4..b87e59395 100644 --- a/src/routes/webhooks/gitlab/events.ts +++ b/src/routes/webhooks/gitlab/events.ts @@ -1,5 +1,5 @@ import { getTeam, getUserDetails, getDomain, removeDestinationDocker } from '$lib/common'; -import { checkContainer, removeProxyConfiguration } from '$lib/haproxy'; +import { checkContainer } from '$lib/haproxy'; import * as db from '$lib/database'; import type { RequestHandler } from '@sveltejs/kit'; import cuid from 'cuid'; @@ -151,7 +151,6 @@ export const post: RequestHandler = async (event) => { const engine = applicationFound.destinationDocker.engine; await removeDestinationDocker({ id, engine }); - await removeProxyConfiguration(fqdn); } return { From 78ce8100a38b86a093d94e0913906f7c79f99311 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 11:19:40 +0100 Subject: [PATCH 09/20] migrate file --- prisma/migrations/20220301101928_proxyhash/migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20220301101928_proxyhash/migration.sql diff --git a/prisma/migrations/20220301101928_proxyhash/migration.sql b/prisma/migrations/20220301101928_proxyhash/migration.sql new file mode 100644 index 000000000..87845b38b --- /dev/null +++ b/prisma/migrations/20220301101928_proxyhash/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Setting" ADD COLUMN "proxyHash" TEXT; From 6da78cd3e5ade4915986184eee105a24adbc765b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 11:45:29 +0100 Subject: [PATCH 10/20] remove proxy refresh --- src/lib/haproxy/configuration.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index 26b76d3fd..257bfe376 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -71,7 +71,6 @@ frontend stats bind *:8404 stats enable stats uri / - stats refresh 5s stats admin if TRUE stats auth "\${HAPROXY_USERNAME}:\${HAPROXY_PASSWORD}" @@ -141,7 +140,7 @@ export async function configureHAProxy() { if (isRunning) { data.applications.push({ id, - port, + port: port || 3000, domain, isRunning, isHttps, @@ -165,7 +164,7 @@ export async function configureHAProxy() { let previewDomain = `${container.split('-')[1]}.${domain}`; data.applications.push({ id: container, - port, + port: port || 3000, domain: previewDomain, isRunning, isHttps, From 23e12c9c44e1d1761836a640835a3a22cb8fadb3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 1 Mar 2022 13:07:34 +0100 Subject: [PATCH 11/20] fix: ssl + sslrenew --- src/lib/haproxy/configuration.ts | 37 +++----- .../{letsencrypt.ts => letsencrypt/index.ts} | 88 ++++++++++++++++++- src/lib/queues/index.ts | 6 +- src/lib/queues/ssl.ts | 71 +-------------- src/routes/settings/index.svelte | 3 +- 5 files changed, 103 insertions(+), 102 deletions(-) rename src/lib/{letsencrypt.ts => letsencrypt/index.ts} (57%) diff --git a/src/lib/haproxy/configuration.ts b/src/lib/haproxy/configuration.ts index 257bfe376..a881d9f31 100644 --- a/src/lib/haproxy/configuration.ts +++ b/src/lib/haproxy/configuration.ts @@ -6,7 +6,6 @@ import crypto from 'crypto'; import * as db from '$lib/database'; import { checkContainer, checkHAProxy } from '.'; import { asyncExecShell, getDomain, getEngine } from '$lib/common'; -import { letsEncrypt } from '$lib/letsencrypt'; const url = dev ? 'http://localhost:5555' : 'http://coolify-haproxy:5555'; @@ -115,7 +114,6 @@ export async function haproxyInstance() { export async function configureHAProxy() { const haproxy = await haproxyInstance(); await checkHAProxy(haproxy); - const ssls = []; const data = { applications: [], services: [], @@ -147,7 +145,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? domain : 'www.' + domain }); - if (isHttps) ssls.push({ domain, id, isCoolify: false }); } if (previews) { const host = getEngine(engine); @@ -171,7 +168,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? previewDomain : 'www.' + previewDomain }); - if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); } } } @@ -202,17 +198,18 @@ export async function configureHAProxy() { const isHttps = fqdn.startsWith('https://'); const isWWW = fqdn.includes('www.'); const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; - data.services.push({ - id, - port, - publicPort, - domain, - isRunning, - isHttps, - redirectValue, - redirectTo: isWWW ? domain : 'www.' + domain - }); - if (isHttps) ssls.push({ domain, id, isCoolify: false }); + if (isRunning) { + data.services.push({ + id, + port, + publicPort, + domain, + isRunning, + isHttps, + redirectValue, + redirectTo: isWWW ? domain : 'www.' + domain + }); + } } } const { fqdn } = await db.prisma.setting.findFirst(); @@ -229,7 +226,6 @@ export async function configureHAProxy() { redirectValue, redirectTo: isWWW ? domain : 'www.' + domain }); - if (!dev && isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true }); } const output = mustache.render(template, data); const newHash = crypto.createHash('md5').update(output).digest('hex'); @@ -249,13 +245,4 @@ export async function configureHAProxy() { } else { // console.log('HAProxy configuration is up to date'); } - if (ssls.length > 0) { - for (const ssl of ssls) { - if (!dev) { - await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); - } else { - // console.log('Generate ssl for', ssl.domain); - } - } - } } diff --git a/src/lib/letsencrypt.ts b/src/lib/letsencrypt/index.ts similarity index 57% rename from src/lib/letsencrypt.ts rename to src/lib/letsencrypt/index.ts index b3846eba7..43e5d8a63 100644 --- a/src/lib/letsencrypt.ts +++ b/src/lib/letsencrypt/index.ts @@ -1,7 +1,7 @@ -import { dev } from '$app/env'; -import { forceSSLOffApplication, forceSSLOnApplication } from '$lib/haproxy'; -import { asyncExecShell, getEngine } from './common'; +import { asyncExecShell, getDomain, getEngine } from '$lib/common'; +import { checkContainer } from '$lib/haproxy'; import * as db from '$lib/database'; +import { dev } from '$app/env'; import cuid from 'cuid'; import getPort, { portNumbers } from 'get-port'; @@ -13,7 +13,7 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) { const nakedDomain = domain.replace('www.', ''); const wwwDomain = `www.${nakedDomain}`; const randomCuid = cuid(); - const randomPort = 9000; + const randomPort = await getPort({ port: portNumbers(minPort, maxPort) }); let host; let dualCerts = false; @@ -72,3 +72,83 @@ export async function letsEncrypt(domain, id = null, isCoolify = false) { } } } + +export async function generateSSLCerts() { + const ssls = []; + const applications = await db.prisma.application.findMany({ + include: { destinationDocker: true, settings: true } + }); + for (const application of applications) { + const { + fqdn, + id, + destinationDocker: { engine, network }, + settings: { previews } + } = application; + const isRunning = await checkContainer(engine, id); + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + if (previews) { + const host = getEngine(engine); + const { stdout } = await asyncExecShell( + `DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` + ); + const containers = stdout + .trim() + .split('\n') + .filter((a) => a) + .map((c) => c.replace(/"/g, '')); + if (containers.length > 0) { + for (const container of containers) { + let previewDomain = `${container.split('-')[1]}.${domain}`; + if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); + } + } + } + } + const services = await db.prisma.service.findMany({ + include: { + destinationDocker: true, + minio: true, + plausibleAnalytics: true, + vscodeserver: true, + wordpress: true + } + }); + + for (const service of services) { + const { + fqdn, + id, + type, + destinationDocker: { engine } + } = service; + const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + if (found) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isRunning = await checkContainer(engine, id); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + } + } + const { fqdn } = await db.prisma.setting.findFirst(); + if (fqdn) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isHttps) ssls.push({ domain, id: 'coolify', isCoolify: true }); + } + if (ssls.length > 0) { + for (const ssl of ssls) { + if (!dev) { + await letsEncrypt(ssl.domain, ssl.id, ssl.isCoolify); + } else { + console.log('Generate ssl for', ssl.domain); + } + } + } +} diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index 28a080ed3..1ce9411c4 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -86,9 +86,9 @@ 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: 600000 } }); - // await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); + await queue.ssl.add('ssl', {}, { repeat: { every: 60000 } }); + await queue.cleanup.add('cleanup', {}, { repeat: { every: 600000 } }); + await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); const events = { proxy: new QueueEvents('proxy', { ...connectionOptions }), diff --git a/src/lib/queues/ssl.ts b/src/lib/queues/ssl.ts index cbc7964c0..d14fc0822 100644 --- a/src/lib/queues/ssl.ts +++ b/src/lib/queues/ssl.ts @@ -1,76 +1,9 @@ -import { asyncExecShell, getDomain, getEngine } from '$lib/common'; -import { prisma } from '$lib/database'; -import { dockerInstance } from '$lib/docker'; -import { forceSSLOnApplication } from '$lib/haproxy'; -import * as db from '$lib/database'; -import { dev } from '$app/env'; -import getPort, { portNumbers } from 'get-port'; -import cuid from 'cuid'; +import { generateSSLCerts } from '$lib/letsencrypt'; export default async function () { try { - const data = await db.prisma.setting.findFirst(); - const { minPort, maxPort } = data; - - const publicPort = await getPort({ port: portNumbers(minPort, maxPort) }); - const randomCuid = cuid(); - const destinationDockers = await prisma.destinationDocker.findMany({}); - for (const destination of destinationDockers) { - if (destination.isCoolifyProxyUsed) { - const docker = dockerInstance({ destinationDocker: destination }); - const containers = await docker.engine.listContainers(); - const configurations = containers.filter( - (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 } = parsedConfiguration; - if (fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) { - if (dev) { - console.log('DEV MODE: SSL is enabled'); - } else { - const host = getEngine(destination.engine); - await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm --name certbot-${randomCuid} -p 9080:${publicPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${publicPort} -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); - const { stderr } = await asyncExecShell( - `DOCKER_HOST=${host} docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - } - } - } - } - } - } - } - const { fqdn } = await db.listSettings(); - if (fqdn) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isHttps) { - if (dev) { - console.log('DEV MODE: SSL is enabled'); - } else { - await asyncExecShell( - `docker run --rm --name certbot-${randomCuid} -p 9080:${publicPort} -v "coolify-letsencrypt:/etc/letsencrypt" certbot/certbot --logs-dir /etc/letsencrypt/logs certonly --standalone --preferred-challenges http --http-01-address 0.0.0.0 --http-01-port ${publicPort} -d ${domain} --agree-tos --non-interactive --register-unsafely-without-email` - ); - - const { stderr } = await asyncExecShell( - `docker run --rm -v "coolify-letsencrypt:/etc/letsencrypt" -v "coolify-ssl-certs:/app/ssl" alpine:latest cat /etc/letsencrypt/live/${domain}/fullchain.pem /etc/letsencrypt/live/${domain}/privkey.pem > /app/ssl/${domain}.pem` - ); - if (stderr) throw new Error(stderr); - } - } - } + return await generateSSLCerts(); } catch (error) { - console.log(error); throw error; } } diff --git a/src/routes/settings/index.svelte b/src/routes/settings/index.svelte index 0db5122aa..bf6ee64c3 100644 --- a/src/routes/settings/index.svelte +++ b/src/routes/settings/index.svelte @@ -78,6 +78,7 @@ if (fqdn !== settings.fqdn) { await post(`/settings/check.json`, { fqdn }); await post(`/settings.json`, { fqdn }); + return window.location.reload(); } if (minPort !== settings.minPort || maxPort !== settings.maxPort) { await post(`/settings.json`, { minPort, maxPort }); @@ -98,7 +99,7 @@ {#if $session.teamId === '0'}
-
+
Global Settings
- {:else if isUpdateAvailable} + {#if isUpdateAvailable}
-
-
NameValueNeed during buildtime?Action
- - - - - - - - - - {#each applicationSecrets as secret} - {#key secret.id} - - s.name === secret.name)} - isPRMRSecret - name={secret.name} - value={secret.value} - isBuildSecret={secret.isBuildSecret} - on:refresh={refreshSecrets} - /> - - {/key} - {/each} - -
NameValueNeed during buildtime?Action
- +{#if applicationSecrets.length !== 0} +
+ + + + + + + + + + + {#each applicationSecrets as secret} + {#key secret.id} + + s.name === secret.name)} + isPRMRSecret + name={secret.name} + value={secret.value} + isBuildSecret={secret.isBuildSecret} + on:refresh={refreshSecrets} + /> + + {/key} + {/each} + +
NameValueNeed during buildtime?Action
+
+{/if}
Please add secrets to the application first.

These values overwrite application secrets in PR/MR deployments. Useful for creating staging environments." + ? "You can add secrets to PR/MR deployments. Please add secrets to the application first.
Useful for creating staging environments." : "These values overwrite application secrets in PR/MR deployments. Useful for creating staging environments."} />
diff --git a/src/routes/update.json.ts b/src/routes/update.json.ts index 1cfdc5f50..87a728352 100644 --- a/src/routes/update.json.ts +++ b/src/routes/update.json.ts @@ -12,7 +12,7 @@ export const get: RequestHandler = async () => { const versions = await got .get(`https://get.coollabs.io/versions.json?appId=${process.env['COOLIFY_APP_ID']}`) .json(); - const latestVersion = dev ? '10.0.0' : versions['coolify'].main.version; + const latestVersion = versions['coolify'].main.version; const isUpdateAvailable = compare(latestVersion, currentVersion); return { body: { @@ -26,27 +26,11 @@ export const get: RequestHandler = async () => { }; export const post: RequestHandler = async (event) => { - const { type, latestVersion } = await event.request.json(); - if (type === 'pull') { + const { type, latestVersion, overrideVersion } = await event.request.json(); + if (type === 'update') { try { if (!dev) { await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`); - return { - status: 200, - body: {} - }; - } else { - return { - status: 200, - body: {} - }; - } - } catch (error) { - return ErrorHandler(error); - } - } else if (type === 'update') { - try { - if (!dev) { await asyncExecShell(`env | grep COOLIFY > .env`); await asyncExecShell( `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-redis && docker rm coolify coolify-redis && docker compose up -d --force-recreate"` @@ -66,6 +50,31 @@ export const post: RequestHandler = async (event) => { } catch (error) { return ErrorHandler(error); } + } else if (type === 'check') { + try { + if (overrideVersion) { + return { + status: 200, + body: { + exists: true + } + }; + } + await asyncExecShell(`docker image inspect coollabsio/coolify:${latestVersion}`); + return { + status: 200, + body: { + exists: true + } + }; + } catch (error) { + return { + status: 200, + body: { + exists: false + } + }; + } } return { status: 500