@@ -8,6 +8,8 @@ RUN yarn build
 | 
			
		||||
FROM node:16.14.0-alpine
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
LABEL coolify.managed true
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache git openssh-client curl jq cmake sqlite
 | 
			
		||||
 | 
			
		||||
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							@@ -1,12 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "coolify",
 | 
			
		||||
	"description": "An open-source & self-hostable Heroku / Netlify alternative.",
 | 
			
		||||
	"version": "2.0.4",
 | 
			
		||||
	"version": "2.0.5",
 | 
			
		||||
	"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",
 | 
			
		||||
		"dev:stop": "docker-compose -f docker-compose-dev.yaml down",
 | 
			
		||||
		"dev:logs": "docker-compose -f docker-compose-dev.yaml logs -f --tail 10",
 | 
			
		||||
		"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
 | 
			
		||||
		"dev:stop": "docker compose -f docker-compose-dev.yaml down",
 | 
			
		||||
		"dev:logs": "docker compose -f docker-compose-dev.yaml logs -f --tail 10",
 | 
			
		||||
		"studio": "npx prisma studio",
 | 
			
		||||
		"start": "npx prisma migrate deploy && npx prisma generate && npx prisma db seed && node index.js",
 | 
			
		||||
		"build": "svelte-kit build",
 | 
			
		||||
@@ -16,8 +16,7 @@
 | 
			
		||||
		"db:generate": "prisma generate",
 | 
			
		||||
		"db:push": "prisma db push && prisma generate",
 | 
			
		||||
		"db:seed": "prisma db seed",
 | 
			
		||||
		"stagrelease": "cross-var docker build -t coollabsio/coolify:$npm_package_version . && docker push coollabsio/coolify:$npm_package_version",
 | 
			
		||||
		"prerelease": "cross-var docker build -t coollabsio/coolify:$npm_package_version -t coollabsio/coolify:latest .",
 | 
			
		||||
		"release:staging": "cross-var docker build -t coollabsio/coolify:$npm_package_version . && docker push coollabsio/coolify:$npm_package_version",
 | 
			
		||||
		"release:coolify": "cross-var yarn prerelease && docker push coollabsio/coolify:$npm_package_version && docker image push coollabsio/coolify:$npm_package_version && docker push coollabsio/coolify:latest",
 | 
			
		||||
		"release:haproxy": "docker build -f haproxy.Dockerfile -t coollabsio/coolify-haproxy-alpine:1.0.0 -t coollabsio/coolify-haproxy-alpine:latest . && docker image push --all-tags coollabsio/coolify-haproxy-alpine",
 | 
			
		||||
		"release:haproxy:tcp": "docker build -f haproxy-tcp.Dockerfile -t coollabsio/coolify-haproxy-tcp-alpine:1.0.0 -t coollabsio/coolify-haproxy-tcp-alpine:latest . && docker image push --all-tags coollabsio/coolify-haproxy-tcp-alpine",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,6 @@
 | 
			
		||||
		<meta charset="utf-8" />
 | 
			
		||||
		<link rel="icon" href="/favicon.png" />
 | 
			
		||||
		<meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
			
		||||
		<link rel="dns-prefetch" href="https://cdn.coollabs.io/" />
 | 
			
		||||
		<link rel="preconnect" href="https://cdn.coollabs.io/" crossorigin="" />
 | 
			
		||||
		<link rel="stylesheet" href="https://cdn.coollabs.io/fonts/poppins/poppins.css" />
 | 
			
		||||
		<title>Coolify</title>
 | 
			
		||||
		%svelte.head%
 | 
			
		||||
	</head>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@
 | 
			
		||||
<img
 | 
			
		||||
	alt="minio logo"
 | 
			
		||||
	class={isAbsolute ? 'w-7 absolute top-0 left-0 -m-3 -mt-5' : 'w-4  mx-auto'}
 | 
			
		||||
	src="https://cdn.coollabs.io/assets/coolify/services/minio/MINIO_Bird.png"
 | 
			
		||||
	src="/minio.png"
 | 
			
		||||
/>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@
 | 
			
		||||
<img
 | 
			
		||||
	alt="nocodb logo"
 | 
			
		||||
	class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
 | 
			
		||||
	src="https://cdn.coollabs.io/assets/coolify/services/nocodb/nocodb.png"
 | 
			
		||||
	src="/nocodb.png"
 | 
			
		||||
/>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@
 | 
			
		||||
<img
 | 
			
		||||
	alt="plausible logo"
 | 
			
		||||
	class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-6 mx-auto'}
 | 
			
		||||
	src="https://cdn.coollabs.io/assets/coolify/services/plausible/logo_sm.png"
 | 
			
		||||
	src="/plausible.png"
 | 
			
		||||
/>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								src/lib/components/svg/services/VaultWarden.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/lib/components/svg/services/VaultWarden.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	export let isAbsolute = false;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
	class={isAbsolute ? 'w-10 absolute top-0 left-0 -m-5' : 'w-8 mx-auto'}
 | 
			
		||||
	xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
	xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
	version="1.1"
 | 
			
		||||
	id="Icon"
 | 
			
		||||
	x="0px"
 | 
			
		||||
	y="0px"
 | 
			
		||||
	viewBox="0 0 1024 1024"
 | 
			
		||||
	style="enable-background:new 0 0 1024 1024;"
 | 
			
		||||
	xml:space="preserve"
 | 
			
		||||
>
 | 
			
		||||
	<style type="text/css">
 | 
			
		||||
		.st0 {
 | 
			
		||||
			fill: #175ddc;
 | 
			
		||||
		}
 | 
			
		||||
		.st1 {
 | 
			
		||||
			fill: #ffffff;
 | 
			
		||||
		}
 | 
			
		||||
	</style>
 | 
			
		||||
	<path
 | 
			
		||||
		id="Background"
 | 
			
		||||
		class="st0"
 | 
			
		||||
		d="M1024,864c0,88.4-71.6,160-160,160H160C71.6,1024,0,952.4,0,864V160C0,71.6,71.6,0,160,0h704  c88.4,0,160,71.6,160,160V864z"
 | 
			
		||||
	/>
 | 
			
		||||
	<path
 | 
			
		||||
		id="Identity"
 | 
			
		||||
		class="st1"
 | 
			
		||||
		d="M829.8,128.6c-6.5-6.5-14.2-9.7-23-9.7H217.2c-8.9,0-16.5,3.2-23,9.7c-6.5,6.5-9.7,14.2-9.7,23  v393.1c0,29.3,5.7,58.4,17.1,87.3c11.4,28.8,25.6,54.4,42.5,76.8c16.9,22.3,37,44.1,60.4,65.3c23.4,21.2,45,38.7,64.7,52.7  c19.8,14,40.4,27.2,61.9,39.7c21.5,12.5,36.8,20.9,45.8,25.3c9,4.4,16.3,7.9,21.7,10.2c4.1,2,8.5,3.1,13.3,3.1c4.8,0,9.2-1,13.3-3.1  c5.5-2.4,12.7-5.8,21.8-10.2c9-4.4,24.3-12.9,45.8-25.3c21.5-12.5,42.1-25.7,61.9-39.7c19.8-14,41.4-31.6,64.8-52.7  c23.4-21.2,43.5-42.9,60.4-65.3c16.9-22.4,31-47.9,42.5-76.8c11.4-28.8,17.1-57.9,17.1-87.3V151.7  C839.6,142.8,836.3,135.1,829.8,128.6z M753.8,548.4c0,142.3-241.8,264.9-241.8,264.9V203.1h241.8  C753.8,203.1,753.8,406.1,753.8,548.4z"
 | 
			
		||||
	/>
 | 
			
		||||
</svg>
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { decrypt, encrypt } from '$lib/crypto';
 | 
			
		||||
import { removeProxyConfiguration } from '$lib/haproxy';
 | 
			
		||||
import { asyncExecShell, getEngine, removeContainer } from '$lib/common';
 | 
			
		||||
import { asyncExecShell, getEngine } from '$lib/common';
 | 
			
		||||
 | 
			
		||||
import { getDomain, removeDestinationDocker } from '$lib/common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { getDomain } from '$lib/common';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function isBranchAlreadyUsed({ repository, branch, id }) {
 | 
			
		||||
	const application = await prisma.application.findUnique({
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,12 @@ export const supportedServiceTypesAndVersions = [
 | 
			
		||||
		fancyName: 'Wordpress',
 | 
			
		||||
		baseImage: 'wordpress',
 | 
			
		||||
		versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3']
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		name: 'vaultwarden',
 | 
			
		||||
		fancyName: 'Vaultwarden',
 | 
			
		||||
		baseImage: 'vaultwarden/server',
 | 
			
		||||
		versions: ['latest']
 | 
			
		||||
	}
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -114,15 +114,6 @@ export async function updateDatabase({
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// export async function setDatabaseSettings({ id, isPublic }) {
 | 
			
		||||
//     try {
 | 
			
		||||
//         await prisma.databaseSettings.update({ where: { databaseId: id }, data: { isPublic } })
 | 
			
		||||
//         return { status: 201 }
 | 
			
		||||
//     } catch (e) {
 | 
			
		||||
//         throw PrismaErrorHandler(e)
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
export async function stopDatabase(database) {
 | 
			
		||||
	let everStarted = false;
 | 
			
		||||
	const {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import { asyncExecShell, getEngine } from '$lib/common';
 | 
			
		||||
import { dockerInstance } from '$lib/docker';
 | 
			
		||||
import { defaultProxyImageHttp, defaultProxyImageTcp, startCoolifyProxy } from '$lib/haproxy';
 | 
			
		||||
import { startCoolifyProxy } from '$lib/haproxy';
 | 
			
		||||
import { getDatabaseImage } from '.';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function listDestinations(teamId) {
 | 
			
		||||
	return await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId } } } });
 | 
			
		||||
@@ -37,11 +37,9 @@ export async function configureDestinationForDatabase({ id, destinationId }) {
 | 
			
		||||
		const host = getEngine(engine);
 | 
			
		||||
		if (type && version) {
 | 
			
		||||
			const baseImage = getDatabaseImage(type);
 | 
			
		||||
			asyncExecShell(`DOCKER_HOST=${host} docker pull ${baseImage}:${version}`);
 | 
			
		||||
			asyncExecShell(`DOCKER_HOST=${host} docker pull coollabsio/${defaultProxyImageTcp}`);
 | 
			
		||||
			asyncExecShell(`DOCKER_HOST=${host} docker pull coollabsio/${defaultProxyImageHttp}`);
 | 
			
		||||
			asyncExecShell(`DOCKER_HOST=${host} docker pull certbot/certbot:latest`);
 | 
			
		||||
			asyncExecShell(`DOCKER_HOST=${host} docker pull alpine:latest`);
 | 
			
		||||
			asyncExecShell(
 | 
			
		||||
				`DOCKER_HOST=${host} docker pull ${baseImage}:${version} && echo "FROM ${baseImage}:${version}" | docker build --label coolify.managed="true" -t "${baseImage}:${version}" -`
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { decrypt, encrypt } from '$lib/crypto';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function listSources(teamId) {
 | 
			
		||||
	return await prisma.gitSource.findMany({
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { decrypt, encrypt } from '$lib/crypto';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function addInstallation({ gitSourceId, installation_id }) {
 | 
			
		||||
	const source = await prisma.gitSource.findUnique({
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { encrypt } from '$lib/crypto';
 | 
			
		||||
import { generateSshKeyPair, prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { generateSshKeyPair, prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function updateDeployKey({ id, deployKeyId }) {
 | 
			
		||||
	const application = await prisma.application.findUnique({
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ export async function listLogs({ buildId, last = 0 }) {
 | 
			
		||||
			orderBy: { time: 'asc' }
 | 
			
		||||
		});
 | 
			
		||||
		return [...body];
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		throw PrismaErrorHandler(e);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return PrismaErrorHandler(error);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { encrypt } from '$lib/crypto';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function listSecrets({ applicationId }) {
 | 
			
		||||
	return await prisma.secret.findMany({
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
import { decrypt, encrypt } from '$lib/crypto';
 | 
			
		||||
import { dockerInstance } from '$lib/docker';
 | 
			
		||||
import cuid from 'cuid';
 | 
			
		||||
import { generatePassword } from '.';
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function listServices(teamId) {
 | 
			
		||||
	return await prisma.service.findMany({ where: { teams: { some: { id: teamId } } } });
 | 
			
		||||
@@ -99,6 +98,13 @@ export async function configureServiceType({ id, type }) {
 | 
			
		||||
				wordpress: { create: { mysqlPassword, mysqlRootUserPassword, mysqlRootUser, mysqlUser } }
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	} else if (type === 'vaultwarden') {
 | 
			
		||||
		await prisma.service.update({
 | 
			
		||||
			where: { id },
 | 
			
		||||
			data: {
 | 
			
		||||
				type
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
export async function setService({ id, version }) {
 | 
			
		||||
@@ -115,6 +121,9 @@ export async function updatePlausibleAnalyticsService({ id, fqdn, email, usernam
 | 
			
		||||
export async function updateNocoDbOrMinioService({ id, fqdn, name }) {
 | 
			
		||||
	return await prisma.service.update({ where: { id }, data: { fqdn, name } });
 | 
			
		||||
}
 | 
			
		||||
export async function updateVaultWardenService({ id, fqdn, name }) {
 | 
			
		||||
	return await prisma.service.update({ where: { id }, data: { fqdn, name } });
 | 
			
		||||
}
 | 
			
		||||
export async function updateVsCodeServer({ id, fqdn, name }) {
 | 
			
		||||
	return await prisma.service.update({ where: { id }, data: { fqdn, name } });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
 | 
			
		||||
export async function listTeams() {
 | 
			
		||||
	return await prisma.team.findMany();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import cuid from 'cuid';
 | 
			
		||||
import bcrypt from 'bcrypt';
 | 
			
		||||
 | 
			
		||||
import { prisma, PrismaErrorHandler } from './common';
 | 
			
		||||
import { asyncExecShell, removeContainer, uniqueName } from '$lib/common';
 | 
			
		||||
import { prisma } from './common';
 | 
			
		||||
import { asyncExecShell, uniqueName } from '$lib/common';
 | 
			
		||||
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
import { startCoolifyProxy } from '$lib/haproxy';
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,11 @@ export async function forceSSLOffApplication({ domain }) {
 | 
			
		||||
export async function forceSSLOnApplication({ domain }) {
 | 
			
		||||
	if (!dev) {
 | 
			
		||||
		const haproxy = await haproxyInstance();
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
		try {
 | 
			
		||||
			await checkHAProxy(haproxy);
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		const transactionId = await getNextTransactionId();
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
@@ -278,7 +282,11 @@ export async function reloadHaproxy(engine) {
 | 
			
		||||
}
 | 
			
		||||
export async function configureProxyForApplication({ domain, imageId, applicationId, port }) {
 | 
			
		||||
	const haproxy = await haproxyInstance();
 | 
			
		||||
	await checkHAProxy(haproxy);
 | 
			
		||||
	try {
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let serverConfigured = false;
 | 
			
		||||
	let backendAvailable: any = null;
 | 
			
		||||
@@ -358,7 +366,11 @@ export async function configureProxyForApplication({ domain, imageId, applicatio
 | 
			
		||||
 | 
			
		||||
export async function configureCoolifyProxyOff({ domain }) {
 | 
			
		||||
	const haproxy = await haproxyInstance();
 | 
			
		||||
	await checkHAProxy(haproxy);
 | 
			
		||||
	try {
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		const transactionId = await getNextTransactionId();
 | 
			
		||||
@@ -388,7 +400,11 @@ export async function checkHAProxy(haproxy) {
 | 
			
		||||
}
 | 
			
		||||
export async function configureCoolifyProxyOn({ domain }) {
 | 
			
		||||
	const haproxy = await haproxyInstance();
 | 
			
		||||
	await checkHAProxy(haproxy);
 | 
			
		||||
	try {
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	let serverConfigured = false;
 | 
			
		||||
	let backendAvailable: any = null;
 | 
			
		||||
	try {
 | 
			
		||||
@@ -586,11 +602,7 @@ export async function configureNetworkCoolifyProxy(engine) {
 | 
			
		||||
 | 
			
		||||
export async function configureSimpleServiceProxyOn({ id, domain, port }) {
 | 
			
		||||
	const haproxy = await haproxyInstance();
 | 
			
		||||
	try {
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	await checkHAProxy(haproxy);
 | 
			
		||||
	try {
 | 
			
		||||
		await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json();
 | 
			
		||||
		return;
 | 
			
		||||
@@ -627,12 +639,15 @@ export async function configureSimpleServiceProxyOn({ id, domain, port }) {
 | 
			
		||||
 | 
			
		||||
export async function configureSimpleServiceProxyOff({ domain }) {
 | 
			
		||||
	const haproxy = await haproxyInstance();
 | 
			
		||||
	await checkHAProxy(haproxy);
 | 
			
		||||
	try {
 | 
			
		||||
		await checkHAProxy(haproxy);
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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: {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { asyncExecShell, createDirectories, getDomain, getEngine, saveBuildLog }
 | 
			
		||||
import { configureProxyForApplication, reloadHaproxy } from '../haproxy';
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
import { decrypt } from '$lib/crypto';
 | 
			
		||||
import { sentry } from '$lib/common';
 | 
			
		||||
import {
 | 
			
		||||
	copyBaseConfigurationFiles,
 | 
			
		||||
	makeLabelForStandaloneApplication,
 | 
			
		||||
@@ -246,19 +247,22 @@ export default async function (job) {
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			throw new Error(error);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
 | 
			
		||||
			saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
 | 
			
		||||
			await configureProxyForApplication({ domain, imageId, applicationId, port });
 | 
			
		||||
			if (isHttps) await letsEncrypt({ domain, id: applicationId });
 | 
			
		||||
			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
 | 
			
		||||
			});
 | 
			
		||||
		try {
 | 
			
		||||
			if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) {
 | 
			
		||||
				saveBuildLog({ line: 'Proxy configuration started!', buildId, applicationId });
 | 
			
		||||
				await configureProxyForApplication({ domain, imageId, applicationId, port });
 | 
			
		||||
				if (isHttps) await letsEncrypt({ domain, id: applicationId });
 | 
			
		||||
				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) {
 | 
			
		||||
			sentry.captureException(error);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,43 @@
 | 
			
		||||
import { dev } from '$app/env';
 | 
			
		||||
import { asyncExecShell, getEngine } from '$lib/common';
 | 
			
		||||
import { prisma } from '$lib/database';
 | 
			
		||||
import { defaultProxyImageHttp, defaultProxyImageTcp } from '$lib/haproxy';
 | 
			
		||||
 | 
			
		||||
export default async function () {
 | 
			
		||||
	if (!dev) {
 | 
			
		||||
		const destinationDockers = await prisma.destinationDocker.findMany();
 | 
			
		||||
		for (const destinationDocker of destinationDockers) {
 | 
			
		||||
			const host = getEngine(destinationDocker.engine);
 | 
			
		||||
			// Tagging images with labels
 | 
			
		||||
			try {
 | 
			
		||||
				// await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
 | 
			
		||||
				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) {
 | 
			
		||||
					await asyncExecShell(
 | 
			
		||||
						`DOCKER_HOST=${host} docker pull ${image} && echo "FROM ${image}" | docker build --label coolify.managed="true" -t "${image}" -`
 | 
			
		||||
					);
 | 
			
		||||
				}
 | 
			
		||||
			} catch (error) {}
 | 
			
		||||
			try {
 | 
			
		||||
				await asyncExecShell(`DOCKER_HOST=${host} docker container prune -f`);
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				//
 | 
			
		||||
				console.log(error);
 | 
			
		||||
			}
 | 
			
		||||
			// Cleanup images that are not managed by coolify
 | 
			
		||||
			try {
 | 
			
		||||
				// await asyncExecShell(`DOCKER_HOST=${host} docker image prune -f`);
 | 
			
		||||
				await asyncExecShell(
 | 
			
		||||
					`DOCKER_HOST=${host} docker image prune --filter 'label!=coolify.managed=true' -a -f`
 | 
			
		||||
				);
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				//
 | 
			
		||||
				console.log(error);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -107,7 +107,7 @@ cron().catch((error) => {
 | 
			
		||||
	console.log(error);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const buildQueueName = dev ? cuid() : 'build_queue';
 | 
			
		||||
const buildQueueName = 'build_queue';
 | 
			
		||||
const buildQueue = new Queue(buildQueueName, connectionOptions);
 | 
			
		||||
const buildWorker = new Worker(buildQueueName, async (job) => await builder(job), {
 | 
			
		||||
	concurrency: 2,
 | 
			
		||||
@@ -120,11 +120,8 @@ buildWorker.on('completed', async (job: Bullmq.Job) => {
 | 
			
		||||
	} catch (err) {
 | 
			
		||||
		console.log(err);
 | 
			
		||||
	} finally {
 | 
			
		||||
		const workdir = `/tmp/build-sources/${job.data.repository}/${job.data.build_id}`;
 | 
			
		||||
		const workdir = `/tmp/build-sources/${job.data.repository}/`;
 | 
			
		||||
		await asyncExecShell(`rm -fr ${workdir}`);
 | 
			
		||||
		await asyncExecShell(
 | 
			
		||||
			`test -f /tmp/build-sources/${job.data.repository}/id.rsa && rm /tmp/build-sources/${job.data.repository}/id.rsa`
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	return;
 | 
			
		||||
});
 | 
			
		||||
@@ -136,11 +133,8 @@ buildWorker.on('failed', async (job: Bullmq.Job, failedReason) => {
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		console.log(error);
 | 
			
		||||
	} finally {
 | 
			
		||||
		const workdir = `/tmp/build-sources/${job.data.repository}/${job.data.build_id}`;
 | 
			
		||||
		const workdir = `/tmp/build-sources/${job.data.repository}`;
 | 
			
		||||
		await asyncExecShell(`rm -fr ${workdir}`);
 | 
			
		||||
		await asyncExecShell(
 | 
			
		||||
			`test -f /tmp/build-sources/${job.data.repository}/id.rsa && rm /tmp/build-sources/${job.data.repository}/id.rsa`
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	saveBuildLog({ line: 'Failed build!', buildId: job.data.build_id, applicationId: job.data.id });
 | 
			
		||||
	saveBuildLog({
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@ export const get: RequestHandler = async (event) => {
 | 
			
		||||
	try {
 | 
			
		||||
		const destination = await db.getDestination({ id, teamId });
 | 
			
		||||
		const settings = await db.listSettings();
 | 
			
		||||
		const state = await checkContainer(destination.engine, 'coolify-haproxy');
 | 
			
		||||
		const state =
 | 
			
		||||
			destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
 | 
			
		||||
		return {
 | 
			
		||||
			status: 200,
 | 
			
		||||
			body: {
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
	import Wordpress from '$lib/components/svg/services/Wordpress.svelte';
 | 
			
		||||
	import { goto } from '$app/navigation';
 | 
			
		||||
	import { post } from '$lib/api';
 | 
			
		||||
	import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
 | 
			
		||||
 | 
			
		||||
	const { id } = $page.params;
 | 
			
		||||
	const from = $page.url.searchParams.get('from');
 | 
			
		||||
@@ -71,6 +72,8 @@
 | 
			
		||||
						<VsCodeServer isAbsolute />
 | 
			
		||||
					{:else if type.name === 'wordpress'}
 | 
			
		||||
						<Wordpress isAbsolute />
 | 
			
		||||
					{:else if type.name === 'vaultwarden'}
 | 
			
		||||
						<VaultWarden isAbsolute />
 | 
			
		||||
					{/if}{type.fancyName}
 | 
			
		||||
				</button>
 | 
			
		||||
			</form>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
	import Wordpress from '$lib/components/svg/services/Wordpress.svelte';
 | 
			
		||||
	import Services from './_Services/_Services.svelte';
 | 
			
		||||
	import { getDomain } from '$lib/components/common';
 | 
			
		||||
	import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
 | 
			
		||||
 | 
			
		||||
	export let service;
 | 
			
		||||
	export let isRunning;
 | 
			
		||||
@@ -94,6 +95,10 @@
 | 
			
		||||
			<a href="https://wordpress.org" target="_blank">
 | 
			
		||||
				<Wordpress />
 | 
			
		||||
			</a>
 | 
			
		||||
		{:else if service.type === 'vaultwarden'}
 | 
			
		||||
			<a href="https://github.com/dani-garcia/vaultwarden" target="_blank">
 | 
			
		||||
				<VaultWarden />
 | 
			
		||||
			</a>
 | 
			
		||||
		{/if}
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								src/routes/services/[id]/vaultwarden/index.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/routes/services/[id]/vaultwarden/index.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import { getUserDetails } from '$lib/common';
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
import { PrismaErrorHandler } from '$lib/database';
 | 
			
		||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
			
		||||
 | 
			
		||||
export const post: RequestHandler = async (event) => {
 | 
			
		||||
	const { status, body } = await getUserDetails(event);
 | 
			
		||||
	if (status === 401) return { status, body };
 | 
			
		||||
	const { id } = event.params;
 | 
			
		||||
 | 
			
		||||
	let { name, fqdn } = await event.request.json();
 | 
			
		||||
	if (fqdn) fqdn = fqdn.toLowerCase();
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		await db.updateVaultWardenService({ id, fqdn, name });
 | 
			
		||||
		return { status: 201 };
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return PrismaErrorHandler(error);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										83
									
								
								src/routes/services/[id]/vaultwarden/start.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/routes/services/[id]/vaultwarden/start.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
import { asyncExecShell, createDirectories, getEngine, getUserDetails } from '$lib/common';
 | 
			
		||||
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 { configureSimpleServiceProxyOn, reloadHaproxy } from '$lib/haproxy';
 | 
			
		||||
import { getDomain } from '$lib/components/common';
 | 
			
		||||
import { getServiceImage, PrismaErrorHandler } from '$lib/database';
 | 
			
		||||
 | 
			
		||||
export const post: RequestHandler = async (event) => {
 | 
			
		||||
	const { teamId, status, body } = await getUserDetails(event);
 | 
			
		||||
	if (status === 401) return { status, body };
 | 
			
		||||
 | 
			
		||||
	const { id } = event.params;
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		const service = await db.getService({ id, teamId });
 | 
			
		||||
		const { type, version, fqdn, destinationDockerId, destinationDocker } = service;
 | 
			
		||||
 | 
			
		||||
		const domain = getDomain(fqdn);
 | 
			
		||||
		const isHttps = fqdn.startsWith('https://');
 | 
			
		||||
 | 
			
		||||
		const network = destinationDockerId && destinationDocker.network;
 | 
			
		||||
		const host = getEngine(destinationDocker.engine);
 | 
			
		||||
 | 
			
		||||
		const { workdir } = await createDirectories({ repository: type, buildId: id });
 | 
			
		||||
		const baseImage = getServiceImage(type);
 | 
			
		||||
 | 
			
		||||
		const config = {
 | 
			
		||||
			image: `${baseImage}:${version}`,
 | 
			
		||||
			volume: `${id}-vaultwarden-data:/data/`
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		const composeFile = {
 | 
			
		||||
			version: '3.8',
 | 
			
		||||
			services: {
 | 
			
		||||
				[id]: {
 | 
			
		||||
					container_name: id,
 | 
			
		||||
					image: config.image,
 | 
			
		||||
					networks: [network],
 | 
			
		||||
					volumes: [config.volume],
 | 
			
		||||
					restart: 'always'
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			networks: {
 | 
			
		||||
				[network]: {
 | 
			
		||||
					external: true
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			volumes: {
 | 
			
		||||
				[config.volume.split(':')[0]]: {
 | 
			
		||||
					external: true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
		const composeFileDestination = `${workdir}/docker-compose.yaml`;
 | 
			
		||||
		await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
 | 
			
		||||
		try {
 | 
			
		||||
			await asyncExecShell(
 | 
			
		||||
				`DOCKER_HOST=${host} docker volume create ${config.volume.split(':')[0]}`
 | 
			
		||||
			);
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			console.log(error);
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
 | 
			
		||||
			await configureSimpleServiceProxyOn({ id, domain, port: 80 });
 | 
			
		||||
 | 
			
		||||
			if (isHttps) {
 | 
			
		||||
				await letsEncrypt({ domain, id });
 | 
			
		||||
			}
 | 
			
		||||
			await reloadHaproxy(destinationDocker.engine);
 | 
			
		||||
			return {
 | 
			
		||||
				status: 200
 | 
			
		||||
			};
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			return PrismaErrorHandler(error);
 | 
			
		||||
		}
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return PrismaErrorHandler(error);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										39
									
								
								src/routes/services/[id]/vaultwarden/stop.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/routes/services/[id]/vaultwarden/stop.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
 | 
			
		||||
import { getDomain } from '$lib/components/common';
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
import { PrismaErrorHandler } from '$lib/database';
 | 
			
		||||
import { dockerInstance } from '$lib/docker';
 | 
			
		||||
import { checkContainer, configureSimpleServiceProxyOff } 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 { id } = event.params;
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		const service = await db.getService({ id, teamId });
 | 
			
		||||
		const { destinationDockerId, destinationDocker, fqdn } = service;
 | 
			
		||||
		const domain = getDomain(fqdn);
 | 
			
		||||
		if (destinationDockerId) {
 | 
			
		||||
			const engine = destinationDocker.engine;
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				const found = await checkContainer(engine, id);
 | 
			
		||||
				if (found) {
 | 
			
		||||
					await removeDestinationDocker({ id, engine });
 | 
			
		||||
				}
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				console.error(error);
 | 
			
		||||
			}
 | 
			
		||||
			await configureSimpleServiceProxyOff({ domain });
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			status: 200
 | 
			
		||||
		};
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return PrismaErrorHandler(error);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
	import MinIo from '$lib/components/svg/services/MinIO.svelte';
 | 
			
		||||
	import VsCodeServer from '$lib/components/svg/services/VSCodeServer.svelte';
 | 
			
		||||
	import Wordpress from '$lib/components/svg/services/Wordpress.svelte';
 | 
			
		||||
	import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
 | 
			
		||||
 | 
			
		||||
	export let services;
 | 
			
		||||
</script>
 | 
			
		||||
@@ -67,6 +68,8 @@
 | 
			
		||||
						<VsCodeServer isAbsolute />
 | 
			
		||||
					{:else if service.type === 'wordpress'}
 | 
			
		||||
						<Wordpress isAbsolute />
 | 
			
		||||
					{:else if service.type === 'vaultwarden'}
 | 
			
		||||
						<VaultWarden isAbsolute />
 | 
			
		||||
					{/if}
 | 
			
		||||
					<div class="font-bold text-xl text-center truncate">
 | 
			
		||||
						{service.name}
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,9 @@
 | 
			
		||||
						class:hover:bg-orange-500={!loading}
 | 
			
		||||
						disabled={loading}>{loading ? 'Saving...' : 'Save'}</button
 | 
			
		||||
					>
 | 
			
		||||
					<button on:click={() => installRepositories(source)}>Change GitHub App Settings</button>
 | 
			
		||||
					<button on:click|preventDefault={() => installRepositories(source)}
 | 
			
		||||
						>Change GitHub App Settings</button
 | 
			
		||||
					>
 | 
			
		||||
				{/if}
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="grid grid-flow-row gap-2 px-10">
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,25 @@
 | 
			
		||||
@tailwind components;
 | 
			
		||||
@tailwind utilities;
 | 
			
		||||
 | 
			
		||||
/* poppins-regular - latin-ext_latin_devanagari */
 | 
			
		||||
@font-face {
 | 
			
		||||
	font-family: 'Poppins';
 | 
			
		||||
	font-style: normal;
 | 
			
		||||
	font-weight: 400;
 | 
			
		||||
	src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-regular.woff2') format('woff2'),
 | 
			
		||||
		/* Chrome 26+, Opera 23+, Firefox 39+ */
 | 
			
		||||
			url('/poppins-v19-latin-ext_latin_devanagari-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
 | 
			
		||||
}
 | 
			
		||||
/* poppins-500 - latin-ext_latin_devanagari */
 | 
			
		||||
@font-face {
 | 
			
		||||
	font-family: 'Poppins';
 | 
			
		||||
	font-style: normal;
 | 
			
		||||
	font-weight: 500;
 | 
			
		||||
	src: local(''), url('/poppins-v19-latin-ext_latin_devanagari-500.woff2') format('woff2'),
 | 
			
		||||
		/* Chrome 26+, Opera 23+, Firefox 39+ */ url('/poppins-v19-latin-ext_latin_devanagari-500.woff')
 | 
			
		||||
			format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html {
 | 
			
		||||
	@apply h-full min-h-full;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								static/minio.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/minio.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 7.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/nocodb.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/nocodb.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/plausible.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/plausible.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-500.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-500.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-500.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-500.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-regular.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-regular.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-regular.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/poppins-v19-latin-ext_latin_devanagari-regular.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user