diff --git a/Dockerfile b/Dockerfile index 83d513cec..9ca82df2c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/package.json b/package.json index db46808ff..b34df3e5a 100644 --- a/package.json +++ b/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", diff --git a/src/app.html b/src/app.html index d5c431e11..2dc97f37d 100644 --- a/src/app.html +++ b/src/app.html @@ -4,9 +4,6 @@ - - - Coolify %svelte.head% diff --git a/src/lib/components/svg/services/MinIO.svelte b/src/lib/components/svg/services/MinIO.svelte index f9688699f..b6c535357 100644 --- a/src/lib/components/svg/services/MinIO.svelte +++ b/src/lib/components/svg/services/MinIO.svelte @@ -5,5 +5,5 @@ minio logo diff --git a/src/lib/components/svg/services/NocoDB.svelte b/src/lib/components/svg/services/NocoDB.svelte index 3e61a2b8f..ba93c4f78 100644 --- a/src/lib/components/svg/services/NocoDB.svelte +++ b/src/lib/components/svg/services/NocoDB.svelte @@ -5,5 +5,5 @@ nocodb logo diff --git a/src/lib/components/svg/services/PlausibleAnalytics.svelte b/src/lib/components/svg/services/PlausibleAnalytics.svelte index 920f2ecf1..9ae5774fc 100644 --- a/src/lib/components/svg/services/PlausibleAnalytics.svelte +++ b/src/lib/components/svg/services/PlausibleAnalytics.svelte @@ -5,5 +5,5 @@ plausible logo diff --git a/src/lib/components/svg/services/VaultWarden.svelte b/src/lib/components/svg/services/VaultWarden.svelte new file mode 100644 index 000000000..3e4f6b8b5 --- /dev/null +++ b/src/lib/components/svg/services/VaultWarden.svelte @@ -0,0 +1,35 @@ + + + + + + + diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index 037334873..ece44bffa 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -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'; diff --git a/src/lib/database/checks.ts b/src/lib/database/checks.ts index 884c6d2bd..38ddc96b9 100644 --- a/src/lib/database/checks.ts +++ b/src/lib/database/checks.ts @@ -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({ diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index c79e9d101..c94dadd28 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -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'] } ]; diff --git a/src/lib/database/databases.ts b/src/lib/database/databases.ts index 00afa4827..6ca187ffb 100644 --- a/src/lib/database/databases.ts +++ b/src/lib/database/databases.ts @@ -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 { diff --git a/src/lib/database/destinations.ts b/src/lib/database/destinations.ts index a4e5ec228..4a8c46ef9 100644 --- a/src/lib/database/destinations.ts +++ b/src/lib/database/destinations.ts @@ -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}" -` + ); } } } diff --git a/src/lib/database/gitSources.ts b/src/lib/database/gitSources.ts index b2f43a092..927907964 100644 --- a/src/lib/database/gitSources.ts +++ b/src/lib/database/gitSources.ts @@ -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({ diff --git a/src/lib/database/github.ts b/src/lib/database/github.ts index 46f57159f..339b8e008 100644 --- a/src/lib/database/github.ts +++ b/src/lib/database/github.ts @@ -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({ diff --git a/src/lib/database/gitlab.ts b/src/lib/database/gitlab.ts index e8020794b..eb9bdfb1b 100644 --- a/src/lib/database/gitlab.ts +++ b/src/lib/database/gitlab.ts @@ -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({ diff --git a/src/lib/database/logs.ts b/src/lib/database/logs.ts index e4dd7a3e6..0b92654b7 100644 --- a/src/lib/database/logs.ts +++ b/src/lib/database/logs.ts @@ -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); } } diff --git a/src/lib/database/secrets.ts b/src/lib/database/secrets.ts index 06f6592b2..c03ec9459 100644 --- a/src/lib/database/secrets.ts +++ b/src/lib/database/secrets.ts @@ -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({ diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts index 6b7ac2f3f..5568cd4a7 100644 --- a/src/lib/database/services.ts +++ b/src/lib/database/services.ts @@ -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 } }); } diff --git a/src/lib/database/teams.ts b/src/lib/database/teams.ts index 3e1cf9928..dd9a14cff 100644 --- a/src/lib/database/teams.ts +++ b/src/lib/database/teams.ts @@ -1,4 +1,4 @@ -import { prisma, PrismaErrorHandler } from './common'; +import { prisma } from './common'; export async function listTeams() { return await prisma.team.findMany(); diff --git a/src/lib/database/users.ts b/src/lib/database/users.ts index 663ede1a4..7b15ff4d2 100644 --- a/src/lib/database/users.ts +++ b/src/lib/database/users.ts @@ -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'; diff --git a/src/lib/haproxy/index.ts b/src/lib/haproxy/index.ts index 2b811967a..7679fe28f 100644 --- a/src/lib/haproxy/index.ts +++ b/src/lib/haproxy/index.ts @@ -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: { diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 9430e786e..aba494d92 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -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); } } } diff --git a/src/lib/queues/cleanup.ts b/src/lib/queues/cleanup.ts index 14a1d3fa3..e7bd31e1f 100644 --- a/src/lib/queues/cleanup.ts +++ b/src/lib/queues/cleanup.ts @@ -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); } } diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index 8a0d0ecb8..e1889bd4f 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -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({ diff --git a/src/routes/destinations/[id]/index.json.ts b/src/routes/destinations/[id]/index.json.ts index 3b878f9ca..e6fc9d075 100644 --- a/src/routes/destinations/[id]/index.json.ts +++ b/src/routes/destinations/[id]/index.json.ts @@ -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: { diff --git a/src/routes/services/[id]/configuration/type.svelte b/src/routes/services/[id]/configuration/type.svelte index 5fb0a0559..398f6a4d5 100644 --- a/src/routes/services/[id]/configuration/type.svelte +++ b/src/routes/services/[id]/configuration/type.svelte @@ -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 @@ {:else if type.name === 'wordpress'} + {:else if type.name === 'vaultwarden'} + {/if}{type.fancyName} diff --git a/src/routes/services/[id]/index.svelte b/src/routes/services/[id]/index.svelte index 06dace107..1682e5b25 100644 --- a/src/routes/services/[id]/index.svelte +++ b/src/routes/services/[id]/index.svelte @@ -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 @@ + {:else if service.type === 'vaultwarden'} + + + {/if} diff --git a/src/routes/services/[id]/vaultwarden/index.json.ts b/src/routes/services/[id]/vaultwarden/index.json.ts new file mode 100644 index 000000000..49d89b207 --- /dev/null +++ b/src/routes/services/[id]/vaultwarden/index.json.ts @@ -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); + } +}; diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts new file mode 100644 index 000000000..a84366092 --- /dev/null +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -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); + } +}; diff --git a/src/routes/services/[id]/vaultwarden/stop.json.ts b/src/routes/services/[id]/vaultwarden/stop.json.ts new file mode 100644 index 000000000..64a3141b3 --- /dev/null +++ b/src/routes/services/[id]/vaultwarden/stop.json.ts @@ -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); + } +}; diff --git a/src/routes/services/index.svelte b/src/routes/services/index.svelte index 1800c5ed1..997c8d313 100644 --- a/src/routes/services/index.svelte +++ b/src/routes/services/index.svelte @@ -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; @@ -67,6 +68,8 @@ {:else if service.type === 'wordpress'} + {:else if service.type === 'vaultwarden'} + {/if}
{service.name} diff --git a/src/routes/sources/[id]/_Github.svelte b/src/routes/sources/[id]/_Github.svelte index b3285fd6d..91883d298 100644 --- a/src/routes/sources/[id]/_Github.svelte +++ b/src/routes/sources/[id]/_Github.svelte @@ -73,7 +73,9 @@ class:hover:bg-orange-500={!loading} disabled={loading}>{loading ? 'Saving...' : 'Save'} - + {/if}
diff --git a/src/tailwind.css b/src/tailwind.css index 3bae89190..a49b0b24a 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -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; } diff --git a/static/minio.png b/static/minio.png new file mode 100644 index 000000000..72c994c68 Binary files /dev/null and b/static/minio.png differ diff --git a/static/nocodb.png b/static/nocodb.png new file mode 100644 index 000000000..dec31a6dd Binary files /dev/null and b/static/nocodb.png differ diff --git a/static/plausible.png b/static/plausible.png new file mode 100644 index 000000000..89c7558e8 Binary files /dev/null and b/static/plausible.png differ diff --git a/static/poppins-v19-latin-ext_latin_devanagari-500.woff b/static/poppins-v19-latin-ext_latin_devanagari-500.woff new file mode 100644 index 000000000..d5e713b46 Binary files /dev/null and b/static/poppins-v19-latin-ext_latin_devanagari-500.woff differ diff --git a/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 b/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 new file mode 100644 index 000000000..6e1053fbc Binary files /dev/null and b/static/poppins-v19-latin-ext_latin_devanagari-500.woff2 differ diff --git a/static/poppins-v19-latin-ext_latin_devanagari-regular.woff b/static/poppins-v19-latin-ext_latin_devanagari-regular.woff new file mode 100644 index 000000000..ccce523fe Binary files /dev/null and b/static/poppins-v19-latin-ext_latin_devanagari-regular.woff differ diff --git a/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 b/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 new file mode 100644 index 000000000..7457692a2 Binary files /dev/null and b/static/poppins-v19-latin-ext_latin_devanagari-regular.woff2 differ