diff --git a/package.json b/package.json
index db46808ff..10db9ccc2 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",
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/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/services.ts b/src/lib/database/services.ts
index 6b7ac2f3f..61cf70316 100644
--- a/src/lib/database/services.ts
+++ b/src/lib/database/services.ts
@@ -99,6 +99,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 +122,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/haproxy/index.ts b/src/lib/haproxy/index.ts
index 853bc38b6..7679fe28f 100644
--- a/src/lib/haproxy/index.ts
+++ b/src/lib/haproxy/index.ts
@@ -602,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;
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}