diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 3199008fa..ffed44d2a 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -1,5 +1,5 @@ import { dev } from '$app/env'; -import { asyncExecShell, getEngine } from '$lib/common'; +import { asyncExecShell, getDomain, getEngine } from '$lib/common'; import got from 'got'; import * as db from '$lib/database'; import { letsEncrypt } from '$lib/letsencrypt'; @@ -50,8 +50,8 @@ export async function completeTransaction(transactionId) { } export async function removeProxyConfiguration({ domain }) { - const haproxy = await haproxyInstance(); const transactionId = await getNextTransactionId(); + const haproxy = await haproxyInstance(); const backendFound = await haproxy .get(`v2/services/haproxy/configuration/backends/${domain}`) .json(); @@ -63,8 +63,32 @@ export async function removeProxyConfiguration({ domain }) { } }) .json(); - await completeTransaction(transactionId); } + 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(`${domain}%[capture.req.uri]`) + ); + 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(); + } + } + await completeTransaction(transactionId); } export async function forceSSLOffApplication({ domain }) { if (!dev) { @@ -124,7 +148,9 @@ export async function forceSSLOnApplication({ domain }) { .json(); let nextRule = 0; if (rules.data.length > 0) { - const rule = rules.data.find((rule) => rule.cond_test.includes(`-i ${domain}`)); + 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; } @@ -138,7 +164,7 @@ export async function forceSSLOnApplication({ domain }) { json: { index: nextRule, cond: 'if', - cond_test: `{ hdr(Host) -i ${domain} } !{ ssl_fc }`, + cond_test: `{ hdr(host) -i ${domain} } !{ ssl_fc }`, type: 'redirect', redir_type: 'scheme', redir_value: 'https', @@ -572,3 +598,59 @@ export async function configureSimpleServiceProxyOff({ domain }) { } catch (error) {} return; } + +export async function setWwwRedirection(fqdn) { + const haproxy = await haproxyInstance(); + try { + await checkHAProxy(haproxy); + } catch (error) { + return; + } + const transactionId = await getNextTransactionId(); + + try { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isWWW = fqdn.includes('www.'); + 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(`${domain}%[capture.req.uri]`) + ); + if (rule) return; + nextRule = rules.data[rules.data.length - 1].index + 1; + } + const redirectValue = `${isHttps ? 'https://' : 'http://'}${domain}%[capture.req.uri]`; + await haproxy + .post(`v2/services/haproxy/configuration/http_request_rules`, { + searchParams: { + transaction_id: transactionId, + parent_name: 'http', + parent_type: 'frontend' + }, + json: { + index: nextRule, + cond: `${isWWW ? 'unless' : 'if'}`, + cond_test: `{ hdr_beg(host) -i www }`, + type: 'redirect', + redir_type: 'location', + redir_value: redirectValue, + redir_code: 301 + } + }) + .json(); + } catch (error) { + console.log(error); + throw error; + } finally { + await completeTransaction(transactionId); + } +} diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 89513ebcf..8d35ac8d1 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -4,7 +4,7 @@ import * as buildpacks from '../buildPacks'; import * as importers from '../importers'; import { dockerInstance } from '../docker'; import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog } from '../common'; -import { configureProxyForApplication, reloadHaproxy } from '../haproxy'; +import { configureProxyForApplication, reloadHaproxy, setWwwRedirection } from '../haproxy'; import * as db from '$lib/database'; import { decrypt } from '$lib/crypto'; import { sentry } from '$lib/common'; @@ -248,6 +248,7 @@ export default async function (job) { saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId }); 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 { diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index 91bc1c69f..930088ec5 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -266,7 +266,7 @@ required /> diff --git a/src/routes/services/[id]/_Services/_Services.svelte b/src/routes/services/[id]/_Services/_Services.svelte index 0b1d6f622..6754d31fe 100644 --- a/src/routes/services/[id]/_Services/_Services.svelte +++ b/src/routes/services/[id]/_Services/_Services.svelte @@ -110,25 +110,9 @@ required /> - {#if service.type === 'plausibleanalytics'} diff --git a/src/routes/settings/index.svelte b/src/routes/settings/index.svelte index 48407e8b3..e7235c3b9 100644 --- a/src/routes/settings/index.svelte +++ b/src/routes/settings/index.svelte @@ -118,7 +118,7 @@ required />