fix: traefik config + ui + api

This commit is contained in:
Andras Bacsai
2023-07-18 15:34:05 +02:00
parent 6c0544adb2
commit 6e011025a7
6 changed files with 1384 additions and 1415 deletions

View File

@@ -398,7 +398,9 @@ export async function saveApplication(
dockerComposeFileLocation, dockerComposeFileLocation,
dockerComposeConfiguration, dockerComposeConfiguration,
simpleDockerfile, simpleDockerfile,
dockerRegistryImageName dockerRegistryImageName,
basicAuthPw,
basicAuthUser,
} = request.body; } = request.body;
if (port) port = Number(port); if (port) port = Number(port);
if (exposePort) { if (exposePort) {
@@ -453,6 +455,8 @@ export async function saveApplication(
dockerComposeConfiguration, dockerComposeConfiguration,
simpleDockerfile, simpleDockerfile,
dockerRegistryImageName, dockerRegistryImageName,
basicAuthPw,
basicAuthUser,
...defaultConfiguration, ...defaultConfiguration,
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } } connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
} }
@@ -476,6 +480,8 @@ export async function saveApplication(
dockerComposeFileLocation, dockerComposeFileLocation,
dockerComposeConfiguration, dockerComposeConfiguration,
simpleDockerfile, simpleDockerfile,
basicAuthPw,
basicAuthUser,
dockerRegistryImageName, dockerRegistryImageName,
...defaultConfiguration ...defaultConfiguration
} }
@@ -499,22 +505,16 @@ export async function saveApplicationSettings(
previews, previews,
dualCerts, dualCerts,
autodeploy, autodeploy,
branch,
projectId,
isBot, isBot,
isDBBranching, isDBBranching,
isCustomSSL, isCustomSSL,
isHttp2, isHttp2,
basicAuth, basicAuth,
basicAuthUser,
basicAuthPw
} = request.body; } = request.body;
await prisma.application.update({ await prisma.application.update({
where: { id }, where: { id },
data: { data: {
fqdn: isBot ? null : undefined, fqdn: isBot ? null : undefined,
basicAuthUser,
basicAuthPw,
settings: { settings: {
update: { update: {
debug, debug,

View File

@@ -28,6 +28,8 @@ export interface SaveApplication extends OnlyId {
dockerComposeConfiguration: string; dockerComposeConfiguration: string;
simpleDockerfile: string; simpleDockerfile: string;
dockerRegistryImageName: string; dockerRegistryImageName: string;
basicAuthPw: string;
basicAuthUser: string;
}; };
} }
export interface SaveApplicationSettings extends OnlyId { export interface SaveApplicationSettings extends OnlyId {
@@ -44,8 +46,6 @@ export interface SaveApplicationSettings extends OnlyId {
isCustomSSL: boolean; isCustomSSL: boolean;
isHttp2: boolean; isHttp2: boolean;
basicAuth: boolean; basicAuth: boolean;
basicAuthUser: string;
basicAuthPw: string;
}; };
} }
export interface DeleteApplication extends OnlyId { export interface DeleteApplication extends OnlyId {

View File

@@ -19,8 +19,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify';
import type { Login, Update } from '.'; import type { Login, Update } from '.';
import type { GetCurrentUser } from './types'; import type { GetCurrentUser } from './types';
export async function hashPassword(password: string): Promise<string> { export async function hashPassword(password: string, saltRounds = 15): Promise<string> {
const saltRounds = 15;
return bcrypt.hash(password, saltRounds); return bcrypt.hash(password, saltRounds);
} }
@@ -77,7 +76,7 @@ export async function refreshTags() {
tags = JSON.parse(tags).concat(JSON.parse(testTags)); tags = JSON.parse(tags).concat(JSON.parse(testTags));
} }
} }
} catch (error) {} } catch (error) { }
await fs.writeFile('./tags.json', tags); await fs.writeFile('./tags.json', tags);
} else { } else {
const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text(); const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text();
@@ -102,7 +101,7 @@ export async function refreshTemplates() {
if (await fs.stat('./testTemplate.yaml')) { if (await fs.stat('./testTemplate.yaml')) {
templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8')); templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8'));
} }
} catch (error) {} } catch (error) { }
const response = await fs.readFile('./devTemplates.yaml', 'utf8'); const response = await fs.readFile('./devTemplates.yaml', 'utf8');
await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response))); await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response)));
} else { } else {

View File

@@ -3,6 +3,7 @@ import { errorHandler, executeCommand, getDomain, isDev, prisma } from '../../..
import { getTemplates } from '../../../lib/services'; import { getTemplates } from '../../../lib/services';
import { OnlyId } from '../../../types'; import { OnlyId } from '../../../types';
import { parseAndFindServiceTemplates } from '../../api/v1/services/handlers'; import { parseAndFindServiceTemplates } from '../../api/v1/services/handlers';
import { hashPassword } from '../../api/v1/handlers';
function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps = false) { function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps = false) {
if (isHttp2) { if (isHttp2) {
@@ -39,7 +40,7 @@ function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps
} }
}; };
} }
function generateRouters( async function generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,
@@ -50,19 +51,12 @@ function generateRouters(
isCustomSSL, isCustomSSL,
isHttp2 = false, isHttp2 = false,
basicAuth = false, basicAuth = false,
basicAuthUser = '', httpBasicAuth = null
basicAuthPw = ''
) { ) {
const rule = `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`; const rule = `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`;
const ruleWWW = `Host(\`www.${nakedDomain}\`)${ const ruleWWW = `Host(\`www.${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''
pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : '' }`;
}`;
const httpBasicAuth: any = {
basicauth: {
users: [Buffer.from(basicAuthUser + ':' + basicAuthPw).toString('base64')]
}
};
const http: any = { const http: any = {
entrypoints: ['web'], entrypoints: ['web'],
@@ -233,10 +227,6 @@ function generateRouters(
[`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW } [`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW }
}; };
if (basicAuth) {
result[`${serviceId}-${pathPrefix}-basic-auth`] = { ...httpBasicAuth };
}
return result; return result;
} }
export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote = false) { export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote = false) {
@@ -418,6 +408,12 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
) { ) {
continue; continue;
} }
const httpBasicAuth: any = {
basicAuth: {
users: [basicAuthUser + ':' + await hashPassword(basicAuthPw, 1)]
}
};
if (buildPack === 'compose') { if (buildPack === 'compose') {
const services = Object.entries(JSON.parse(dockerComposeConfiguration)); const services = Object.entries(JSON.parse(dockerComposeConfiguration));
if (services.length > 0) { if (services.length > 0) {
@@ -440,7 +436,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,
@@ -448,13 +444,19 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
isHttps, isHttps,
isWWW, isWWW,
dualCerts, dualCerts,
isCustomSSL isCustomSSL,
httpBasicAuth
) )
}; };
traefik.http.services = { traefik.http.services = {
...traefik.http.services, ...traefik.http.services,
...generateServices(serviceId, containerId, port) ...generateServices(serviceId, containerId, port)
}; };
if (application.settings.basicAuth) {
traefik.http.middlewares[`${serviceId}-${pathPrefix}-basic-auth`] = {
...httpBasicAuth
};
}
} }
} }
} }
@@ -473,7 +475,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const serviceId = `${id}-${port || 'default'}`; const serviceId = `${id}-${port || 'default'}`;
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,
@@ -484,14 +486,18 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
isCustomSSL, isCustomSSL,
isHttp2, isHttp2,
basicAuth, basicAuth,
basicAuthUser, httpBasicAuth
basicAuthPw
) )
}; };
traefik.http.services = { traefik.http.services = {
...traefik.http.services, ...traefik.http.services,
...generateServices(serviceId, id, port, isHttp2, isHttps) ...generateServices(serviceId, id, port, isHttp2, isHttps)
}; };
if (application.settings.basicAuth) {
traefik.http.middlewares[`${serviceId}-${pathPrefix}-basic-auth`] = {
...httpBasicAuth
};
}
if (previews) { if (previews) {
const { stdout } = await executeCommand({ const { stdout } = await executeCommand({
dockerId, dockerId,
@@ -505,15 +511,14 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
.map((c) => c.replace(/"/g, '')); .map((c) => c.replace(/"/g, ''));
if (containers.length > 0) { if (containers.length > 0) {
for (const container of containers) { for (const container of containers) {
const previewDomain = `${container.split('-')[1]}${ const previewDomain = `${container.split('-')[1]}${coolifySettings.previewSeparator
coolifySettings.previewSeparator }${domain}`;
}${domain}`;
const nakedDomain = previewDomain.replace(/^www\./, ''); const nakedDomain = previewDomain.replace(/^www\./, '');
const pathPrefix = '/'; const pathPrefix = '/';
const serviceId = `${container}-${port || 'default'}`; const serviceId = `${container}-${port || 'default'}`;
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
previewDomain, previewDomain,
nakedDomain, nakedDomain,
@@ -524,14 +529,18 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
isCustomSSL, isCustomSSL,
false, false,
basicAuth, basicAuth,
basicAuthUser, httpBasicAuth
basicAuthPw
) )
}; };
traefik.http.services = { traefik.http.services = {
...traefik.http.services, ...traefik.http.services,
...generateServices(serviceId, container, port, isHttp2) ...generateServices(serviceId, container, port, isHttp2)
}; };
if (application.settings.basicAuth) {
traefik.http.middlewares[`${serviceId}-${pathPrefix}-basic-auth`] = {
...httpBasicAuth
};
}
} }
} }
} }
@@ -625,7 +634,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const serviceId = `${oneService}-${port || 'default'}`; const serviceId = `${oneService}-${port || 'default'}`;
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,
@@ -633,7 +642,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
isHttps, isHttps,
isWWW, isWWW,
dualCerts, dualCerts,
isCustomSSL isCustomSSL,
) )
}; };
traefik.http.services = { traefik.http.services = {
@@ -662,7 +671,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const serviceId = `${oneService}-${port || 'default'}`; const serviceId = `${oneService}-${port || 'default'}`;
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,
@@ -703,7 +712,7 @@ export async function proxyConfiguration(request: FastifyRequest<OnlyId>, remote
const serviceId = `${id}-${port || 'default'}`; const serviceId = `${id}-${port || 'default'}`;
traefik.http.routers = { traefik.http.routers = {
...traefik.http.routers, ...traefik.http.routers,
...generateRouters( ...await generateRouters(
serviceId, serviceId,
domain, domain,
nakedDomain, nakedDomain,

View File

@@ -36,6 +36,7 @@
import Beta from '$lib/components/Beta.svelte'; import Beta from '$lib/components/Beta.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import Explainer from '$lib/components/Explainer.svelte';
import Setting from '$lib/components/Setting.svelte'; import Setting from '$lib/components/Setting.svelte';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import { import {
addToast, addToast,
appSession, appSession,
@@ -215,9 +216,7 @@
isHttp2, isHttp2,
branch: application.branch, branch: application.branch,
projectId: application.projectId, projectId: application.projectId,
basicAuth, basicAuth
basicAuthUser: application.basicAuthUser,
basicAuthPw: application.basicAuthPw
}); });
return addToast({ return addToast({
message: $t('application.settings_saved'), message: $t('application.settings_saved'),
@@ -282,6 +281,7 @@
} }
} }
} }
console.log(application);
await saveForm(id, application, baseDatabaseBranch, dockerComposeConfiguration); await saveForm(id, application, baseDatabaseBranch, dockerComposeConfiguration);
setLocation(application, settings); setLocation(application, settings);
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application); $isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
@@ -762,55 +762,6 @@
/> />
</div> </div>
<div class="grid grid-cols-2 items-center">
<Setting
id="basicAuth"
dataTooltip={$t('forms.must_be_stopped_to_modify')}
disabled={isDisabled}
isCenter={false}
bind:setting={basicAuth}
title={$t('application.basic_auth')}
description="Activate basic authentication for your application. <br>Useful if you want to protect your application with a password. <br><br>Use the <span class='font-bold text-settings'>username</span> and <span class='font-bold text-settings'>password</span> fields to set the credentials."
on:click={() => !isDisabled && changeSettings('basicAuth')}
/>
</div>
{#if basicAuth}
<div class="grid grid-cols-2 items-center">
<label for="basicAuthUser">{$t('application.basic_auth_user')}</label>
<input
bind:this={fqdnEl}
class="w-full"
required={!application.settings?.basicAuth}
readonly={isDisabled}
disabled={isDisabled}
name="basicAuthUser"
id="basicAuthUser"
class:border={!application.settings?.basicAuth && !application.basicAuthUser}
class:border-red-500={!application.settings?.basicAuth &&
!application.basicAuthUser}
bind:value={application.basicAuthUser}
placeholder="eg: admin"
/>
</div>
<div class="grid grid-cols-2 items-center">
<label for="basicAuthPw">{$t('application.basic_auth_pw')}</label>
<input
bind:this={fqdnEl}
class="w-full"
required={!application.settings?.basicAuth}
readonly={isDisabled}
disabled={isDisabled}
name="basicAuthPw"
id="basicAuthPw"
class:border={!application.settings?.basicAuth && !application.basicAuthPw}
class:border-red-500={!application.settings?.basicAuth && !application.basicAuthPw}
bind:value={application.basicAuthPw}
placeholder="**********"
/>
</div>
{/if}
{#if isHttps && application.buildPack !== 'compose'} {#if isHttps && application.buildPack !== 'compose'}
<div class="grid grid-cols-2 items-center pb-4"> <div class="grid grid-cols-2 items-center pb-4">
<Setting <Setting
@@ -833,6 +784,46 @@
on:click={() => changeSettings('isHttp2')} on:click={() => changeSettings('isHttp2')}
/> />
</div> </div>
<div class="grid grid-cols-2 items-center">
<Setting
id="basicAuth"
isCenter={false}
bind:setting={basicAuth}
title={$t('application.basic_auth')}
description="Activate basic authentication for your application. <br>Useful if you want to protect your application with a password. <br><br>Use the <span class='font-bold text-settings'>username</span> and <span class='font-bold text-settings'>password</span> fields to set the credentials."
on:click={() => changeSettings('basicAuth')}
/>
</div>
{#if basicAuth}
<div class="grid grid-cols-2 items-center">
<label for="basicAuthUser">{$t('application.basic_auth_user')}</label>
<input
bind:this={fqdnEl}
class="w-full"
required={!application.settings?.basicAuth}
name="basicAuthUser"
id="basicAuthUser"
class:border={!application.settings?.basicAuth && !application.basicAuthUser}
class:border-red-500={!application.settings?.basicAuth &&
!application.basicAuthUser}
bind:value={application.basicAuthUser}
placeholder="eg: admin"
/>
</div>
<div class="grid grid-cols-2 items-center">
<label for="basicAuthPw">{$t('application.basic_auth_pw')}</label>
<CopyPasswordField
bind:this={fqdnEl}
isPasswordField={true}
required={!application.settings?.basicAuth}
name="basicAuthPw"
id="basicAuthPw"
bind:value={application.basicAuthPw}
placeholder="**********"
/>
</div>
{/if}
{/if} {/if}
</div> </div>
{#if isSimpleDockerfile} {#if isSimpleDockerfile}

2610
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff