diff --git a/src/lib/components/svg/services/N8n.svelte b/src/lib/components/svg/services/N8n.svelte
new file mode 100644
index 000000000..04b2e8216
--- /dev/null
+++ b/src/lib/components/svg/services/N8n.svelte
@@ -0,0 +1,24 @@
+
+
+
diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts
index 4638e483e..67316fd06 100644
--- a/src/lib/database/common.ts
+++ b/src/lib/database/common.ts
@@ -165,6 +165,15 @@ export const supportedServiceTypesAndVersions = [
ports: {
main: 8010
}
+ },
+ {
+ name: 'n8n',
+ fancyName: 'n8n',
+ baseImage: 'n8nio/n8n',
+ versions: ['latest'],
+ ports: {
+ main: 5678
+ }
}
];
diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts
index ea0be0a83..4ed11f7fe 100644
--- a/src/lib/database/services.ts
+++ b/src/lib/database/services.ts
@@ -119,6 +119,13 @@ export async function configureServiceType({ id, type }) {
type
}
});
+ } else if (type === 'n8n') {
+ await prisma.service.update({
+ where: { id },
+ data: {
+ type
+ }
+ });
}
}
export async function setServiceVersion({ id, version }) {
@@ -139,7 +146,7 @@ export async function updatePlausibleAnalyticsService({ id, fqdn, email, usernam
await prisma.plausibleAnalytics.update({ where: { serviceId: id }, data: { email, username } });
await prisma.service.update({ where: { id }, data: { name, fqdn } });
}
-export async function updateNocoDbOrMinioService({ id, fqdn, name }) {
+export async function updateService({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
export async function updateLanguageToolService({ id, fqdn, name }) {
diff --git a/src/routes/services/[id]/configuration/type.svelte b/src/routes/services/[id]/configuration/type.svelte
index 0dec45c5f..af10189a2 100644
--- a/src/routes/services/[id]/configuration/type.svelte
+++ b/src/routes/services/[id]/configuration/type.svelte
@@ -38,6 +38,7 @@
import { post } from '$lib/api';
import VaultWarden from '$lib/components/svg/services/VaultWarden.svelte';
import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte';
+ import N8n from '$lib/components/svg/services/N8n.svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
@@ -77,6 +78,8 @@
{:else if type.name === 'languagetool'}
+ {:else if type.name === 'n8n'}
+
{/if}{type.fancyName}
diff --git a/src/routes/services/[id]/index.svelte b/src/routes/services/[id]/index.svelte
index 573ec26ac..6dbf5b6f6 100644
--- a/src/routes/services/[id]/index.svelte
+++ b/src/routes/services/[id]/index.svelte
@@ -39,6 +39,7 @@
import cuid from 'cuid';
import { browser } from '$app/env';
import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte';
+ import N8n from '$lib/components/svg/services/N8n.svelte';
export let service;
export let isRunning;
@@ -109,6 +110,10 @@
+ {:else if service.type === 'n8n'}
+
+
+
{/if}
diff --git a/src/routes/services/[id]/minio/index.json.ts b/src/routes/services/[id]/minio/index.json.ts
index be5c0525f..d717502c5 100644
--- a/src/routes/services/[id]/minio/index.json.ts
+++ b/src/routes/services/[id]/minio/index.json.ts
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
if (fqdn) fqdn = fqdn.toLowerCase();
try {
- await db.updateNocoDbOrMinioService({ id, fqdn, name });
+ await db.updateService({ id, fqdn, name });
return { status: 201 };
} catch (error) {
return ErrorHandler(error);
diff --git a/src/routes/services/[id]/n8n/index.json.ts b/src/routes/services/[id]/n8n/index.json.ts
new file mode 100644
index 000000000..5ec3fa69a
--- /dev/null
+++ b/src/routes/services/[id]/n8n/index.json.ts
@@ -0,0 +1,20 @@
+import { getUserDetails } from '$lib/common';
+import * as db from '$lib/database';
+import { ErrorHandler } 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.updateService({ id, fqdn, name });
+ return { status: 201 };
+ } catch (error) {
+ return ErrorHandler(error);
+ }
+};
diff --git a/src/routes/services/[id]/n8n/start.json.ts b/src/routes/services/[id]/n8n/start.json.ts
new file mode 100644
index 000000000..6c9f63b76
--- /dev/null
+++ b/src/routes/services/[id]/n8n/start.json.ts
@@ -0,0 +1,72 @@
+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 { ErrorHandler, getServiceImage } from '$lib/database';
+import { makeLabelForServices } from '$lib/buildPacks/common';
+
+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, destinationDockerId, destinationDocker, serviceSecret } = service;
+ const network = destinationDockerId && destinationDocker.network;
+ const host = getEngine(destinationDocker.engine);
+
+ const { workdir } = await createDirectories({ repository: type, buildId: id });
+ const image = getServiceImage(type);
+
+ const config = {
+ image: `${image}:${version}`,
+ volume: `${id}-n8n:/root/.n8n`,
+ environmentVariables: {}
+ };
+ if (serviceSecret.length > 0) {
+ serviceSecret.forEach((secret) => {
+ config.environmentVariables[secret.name] = secret.value;
+ });
+ }
+ const composeFile = {
+ version: '3.8',
+ services: {
+ [id]: {
+ container_name: id,
+ image: config.image,
+ networks: [network],
+ volumes: [config.volume],
+ environment: config.environmentVariables,
+ restart: 'always',
+ labels: makeLabelForServices('n8n')
+ }
+ },
+ networks: {
+ [network]: {
+ external: true
+ }
+ },
+ volumes: {
+ [config.volume.split(':')[0]]: {
+ name: config.volume.split(':')[0]
+ }
+ }
+ };
+ const composeFileDestination = `${workdir}/docker-compose.yaml`;
+ await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
+
+ try {
+ await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
+ return {
+ status: 200
+ };
+ } catch (error) {
+ return ErrorHandler(error);
+ }
+ } catch (error) {
+ return ErrorHandler(error);
+ }
+};
diff --git a/src/routes/services/[id]/n8n/stop.json.ts b/src/routes/services/[id]/n8n/stop.json.ts
new file mode 100644
index 000000000..c604e1cc3
--- /dev/null
+++ b/src/routes/services/[id]/n8n/stop.json.ts
@@ -0,0 +1,35 @@
+import { getUserDetails, removeDestinationDocker } from '$lib/common';
+import * as db from '$lib/database';
+import { ErrorHandler } from '$lib/database';
+import { checkContainer } 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;
+ if (destinationDockerId) {
+ const engine = destinationDocker.engine;
+
+ try {
+ const found = await checkContainer(engine, id);
+ if (found) {
+ await removeDestinationDocker({ id, engine });
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ return {
+ status: 200
+ };
+ } catch (error) {
+ return ErrorHandler(error);
+ }
+};
diff --git a/src/routes/services/[id]/nocodb/index.json.ts b/src/routes/services/[id]/nocodb/index.json.ts
index bc9f1a0d1..5ec3fa69a 100644
--- a/src/routes/services/[id]/nocodb/index.json.ts
+++ b/src/routes/services/[id]/nocodb/index.json.ts
@@ -12,7 +12,7 @@ export const post: RequestHandler = async (event) => {
if (fqdn) fqdn = fqdn.toLowerCase();
try {
- await db.updateNocoDbOrMinioService({ id, fqdn, name });
+ await db.updateService({ id, fqdn, name });
return { status: 201 };
} catch (error) {
return ErrorHandler(error);