feat: redirect catch-all rule
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Setting" ADD COLUMN "proxyDefaultRedirect" TEXT;
|
@@ -29,6 +29,7 @@ model Setting {
|
|||||||
proxyPassword String
|
proxyPassword String
|
||||||
proxyUser String
|
proxyUser String
|
||||||
proxyHash String?
|
proxyHash String?
|
||||||
|
proxyDefaultRedirect String?
|
||||||
isAutoUpdateEnabled Boolean @default(false)
|
isAutoUpdateEnabled Boolean @default(false)
|
||||||
isDNSCheckEnabled Boolean @default(true)
|
isDNSCheckEnabled Boolean @default(true)
|
||||||
DNSServers String?
|
DNSServers String?
|
||||||
|
@@ -111,12 +111,13 @@ const host = '0.0.0.0';
|
|||||||
fastify.register(cors);
|
fastify.register(cors);
|
||||||
fastify.register(socketIO, {
|
fastify.register(socketIO, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: "http://localhost:3000"
|
origin: isDev ? "*" : ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// To detect allowed origins
|
// To detect allowed origins
|
||||||
// fastify.addHook('onRequest', async (request, reply) => {
|
// fastify.addHook('onRequest', async (request, reply) => {
|
||||||
|
// console.log(request.headers.host)
|
||||||
// let allowedList = ['coolify:3000'];
|
// let allowedList = ['coolify:3000'];
|
||||||
// const { ipv4, ipv6, fqdn } = await prisma.setting.findFirst({})
|
// const { ipv4, ipv6, fqdn } = await prisma.setting.findFirst({})
|
||||||
|
|
||||||
|
@@ -44,16 +44,18 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
maxPort,
|
maxPort,
|
||||||
isAutoUpdateEnabled,
|
isAutoUpdateEnabled,
|
||||||
isDNSCheckEnabled,
|
isDNSCheckEnabled,
|
||||||
DNSServers
|
DNSServers,
|
||||||
|
proxyDefaultRedirect
|
||||||
} = request.body
|
} = request.body
|
||||||
const { id } = await listSettings();
|
const { id } = await listSettings();
|
||||||
await prisma.setting.update({
|
await prisma.setting.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled }
|
data: { isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled, }
|
||||||
});
|
});
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||||
}
|
}
|
||||||
|
await prisma.setting.update({ where: { id }, data: { proxyDefaultRedirect } });
|
||||||
if (minPort && maxPort) {
|
if (minPort && maxPort) {
|
||||||
await prisma.setting.update({ where: { id }, data: { minPort, maxPort } });
|
await prisma.setting.update({ where: { id }, data: { minPort, maxPort } });
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,8 @@ export interface SaveSettings {
|
|||||||
maxPort: number,
|
maxPort: number,
|
||||||
isAutoUpdateEnabled: boolean,
|
isAutoUpdateEnabled: boolean,
|
||||||
isDNSCheckEnabled: boolean,
|
isDNSCheckEnabled: boolean,
|
||||||
DNSServers: string
|
DNSServers: string,
|
||||||
|
proxyDefaultRedirect: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export interface DeleteDomain {
|
export interface DeleteDomain {
|
||||||
|
@@ -20,12 +20,14 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
|
|||||||
entrypoints: ['web'],
|
entrypoints: ['web'],
|
||||||
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
||||||
service: `${id}`,
|
service: `${id}`,
|
||||||
|
priority: 2,
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
}
|
||||||
let https: any = {
|
let https: any = {
|
||||||
entrypoints: ['websecure'],
|
entrypoints: ['websecure'],
|
||||||
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule: `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
||||||
service: `${id}`,
|
service: `${id}`,
|
||||||
|
priority: 2,
|
||||||
tls: {
|
tls: {
|
||||||
certresolver: 'letsencrypt'
|
certresolver: 'letsencrypt'
|
||||||
},
|
},
|
||||||
@@ -35,12 +37,14 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
|
|||||||
entrypoints: ['web'],
|
entrypoints: ['web'],
|
||||||
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
||||||
service: `${id}`,
|
service: `${id}`,
|
||||||
|
priority: 2,
|
||||||
middlewares: []
|
middlewares: []
|
||||||
}
|
}
|
||||||
let httpsWWW: any = {
|
let httpsWWW: any = {
|
||||||
entrypoints: ['websecure'],
|
entrypoints: ['websecure'],
|
||||||
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
rule: `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`,
|
||||||
service: `${id}`,
|
service: `${id}`,
|
||||||
|
priority: 2,
|
||||||
tls: {
|
tls: {
|
||||||
certresolver: 'letsencrypt'
|
certresolver: 'letsencrypt'
|
||||||
},
|
},
|
||||||
@@ -54,6 +58,7 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
|
|||||||
httpWWW.middlewares.push('redirect-to-non-www');
|
httpWWW.middlewares.push('redirect-to-non-www');
|
||||||
httpsWWW.middlewares.push('redirect-to-non-www');
|
httpsWWW.middlewares.push('redirect-to-non-www');
|
||||||
delete https.tls
|
delete https.tls
|
||||||
|
delete httpsWWW.tls
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. http + www only
|
// 3. http + www only
|
||||||
@@ -64,6 +69,7 @@ function generateRouters(id, domain, nakedDomain, pathPrefix, isHttps, isWWW, is
|
|||||||
http.middlewares.push('redirect-to-www');
|
http.middlewares.push('redirect-to-www');
|
||||||
https.middlewares.push('redirect-to-www');
|
https.middlewares.push('redirect-to-www');
|
||||||
delete https.tls
|
delete https.tls
|
||||||
|
delete httpsWWW.tls
|
||||||
}
|
}
|
||||||
// 5. https + non-www only
|
// 5. https + non-www only
|
||||||
if (isHttps && !isWWW) {
|
if (isHttps && !isWWW) {
|
||||||
@@ -164,6 +170,32 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const { id = null } = request.params;
|
const { id = null } = request.params;
|
||||||
|
const settings = await prisma.setting.findFirst();
|
||||||
|
if (settings.isTraefikUsed && settings.proxyDefaultRedirect) {
|
||||||
|
traefik.http.routers['catchall'] = {
|
||||||
|
entrypoints: ["web"],
|
||||||
|
rule: "HostRegexp(`{catchall:.*}`)",
|
||||||
|
service: "noop",
|
||||||
|
priority: 1,
|
||||||
|
middlewares: ["redirect-regexp"]
|
||||||
|
}
|
||||||
|
traefik.http.middlewares['redirect-regexp'] = {
|
||||||
|
redirectregex: {
|
||||||
|
regex: '(.*)',
|
||||||
|
replacement: settings.proxyDefaultRedirect,
|
||||||
|
permanent: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traefik.http.services['noop'] = {
|
||||||
|
loadBalancer: {
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const sslpath = '/etc/traefik/acme/custom';
|
const sslpath = '/etc/traefik/acme/custom';
|
||||||
|
|
||||||
let certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } } } } })
|
let certificates = await prisma.certificate.findMany({ where: { team: { applications: { some: { settings: { isCustomSSL: true } } }, destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } } } } })
|
||||||
|
@@ -18,16 +18,12 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let settings: any;
|
export let settings: any;
|
||||||
export let certificates: any;
|
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
import { del, get, post } from '$lib/api';
|
import { del, get, post } from '$lib/api';
|
||||||
import { browser } from '$app/env';
|
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
import { addToast, appSession, features } from '$lib/store';
|
import { addToast, appSession, features } from '$lib/store';
|
||||||
import { asyncSleep, errorNotification, getDomain } from '$lib/common';
|
import { asyncSleep, errorNotification, getDomain } from '$lib/common';
|
||||||
import Menu from './_Menu.svelte';
|
|
||||||
import Explainer from '$lib/components/Explainer.svelte';
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
import Upload from '$lib/components/Upload.svelte';
|
|
||||||
|
|
||||||
let isAPIDebuggingEnabled = settings.isAPIDebuggingEnabled;
|
let isAPIDebuggingEnabled = settings.isAPIDebuggingEnabled;
|
||||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||||
@@ -37,6 +33,7 @@
|
|||||||
let DNSServers = settings.DNSServers;
|
let DNSServers = settings.DNSServers;
|
||||||
let minPort = settings.minPort;
|
let minPort = settings.minPort;
|
||||||
let maxPort = settings.maxPort;
|
let maxPort = settings.maxPort;
|
||||||
|
let proxyDefaultRedirect = settings.proxyDefaultRedirect;
|
||||||
|
|
||||||
let forceSave = false;
|
let forceSave = false;
|
||||||
let fqdn = settings.fqdn;
|
let fqdn = settings.fqdn;
|
||||||
@@ -107,6 +104,9 @@
|
|||||||
await post(`/settings`, { fqdn });
|
await post(`/settings`, { fqdn });
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
|
if (proxyDefaultRedirect !== settings.proxyDefaultRedirect) {
|
||||||
|
await post(`/settings`, { proxyDefaultRedirect });
|
||||||
|
}
|
||||||
if (minPort !== settings.minPort || maxPort !== settings.maxPort) {
|
if (minPort !== settings.minPort || maxPort !== settings.maxPort) {
|
||||||
await post(`/settings`, { minPort, maxPort });
|
await post(`/settings`, { minPort, maxPort });
|
||||||
settings.minPort = minPort;
|
settings.minPort = minPort;
|
||||||
@@ -281,6 +281,25 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<div>
|
||||||
|
Default Redirect URL
|
||||||
|
<Explainer
|
||||||
|
position="dropdown-bottom"
|
||||||
|
explanation="You can specify where to redirect all requests that does not have a running resource."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class="w-full"
|
||||||
|
bind:value={proxyDefaultRedirect}
|
||||||
|
readonly={!$appSession.isAdmin}
|
||||||
|
disabled={!$appSession.isAdmin}
|
||||||
|
name="proxyDefaultRedirect"
|
||||||
|
id="proxyDefaultRedirect"
|
||||||
|
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||||
|
placeholder="{$t('forms.eg')}: https://coolify.io"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<Setting
|
<Setting
|
||||||
id="dualCerts"
|
id="dualCerts"
|
||||||
|
Reference in New Issue
Block a user