diff --git a/prisma/migrations/20220402210645_meilisearch/migration.sql b/prisma/migrations/20220402210645_meilisearch/migration.sql
new file mode 100644
index 000000000..9e832b107
--- /dev/null
+++ b/prisma/migrations/20220402210645_meilisearch/migration.sql
@@ -0,0 +1,12 @@
+-- CreateTable
+CREATE TABLE "MeiliSearch" (
+ "id" TEXT NOT NULL PRIMARY KEY,
+ "masterKey" TEXT NOT NULL,
+ "serviceId" TEXT NOT NULL,
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" DATETIME NOT NULL,
+ CONSTRAINT "MeiliSearch_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "MeiliSearch_serviceId_key" ON "MeiliSearch"("serviceId");
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index b7fab7185..986a773bf 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -283,6 +283,7 @@ model Service {
wordpress Wordpress?
ghost Ghost?
serviceSecret ServiceSecret[]
+ meiliSearch MeiliSearch?
}
model PlausibleAnalytics {
@@ -352,3 +353,12 @@ model Ghost {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
+
+model MeiliSearch {
+ id String @id @default(cuid())
+ masterKey String
+ serviceId String @unique
+ service Service @relation(fields: [serviceId], references: [id])
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
diff --git a/src/lib/components/svg/services/MeiliSearch.svelte b/src/lib/components/svg/services/MeiliSearch.svelte
new file mode 100644
index 000000000..b8d4ed788
--- /dev/null
+++ b/src/lib/components/svg/services/MeiliSearch.svelte
@@ -0,0 +1,45 @@
+
+
+
diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts
index 29d04982b..3e473f094 100644
--- a/src/lib/database/common.ts
+++ b/src/lib/database/common.ts
@@ -197,6 +197,16 @@ export const supportedServiceTypesAndVersions = [
ports: {
main: 2368
}
+ },
+ {
+ name: 'meilisearch',
+ fancyName: 'Meilisearch',
+ baseImage: 'getmeili/meilisearch',
+ images: [],
+ versions: ['latest'],
+ ports: {
+ main: 7700
+ }
}
];
diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts
index a5c2ee9ea..c5d221e9e 100644
--- a/src/lib/database/services.ts
+++ b/src/lib/database/services.ts
@@ -22,7 +22,8 @@ export async function getService({ id, teamId }) {
vscodeserver: true,
wordpress: true,
ghost: true,
- serviceSecret: true
+ serviceSecret: true,
+ meiliSearch: true
}
});
@@ -50,6 +51,8 @@ export async function getService({ id, teamId }) {
body.ghost.mariadbRootUserPassword = decrypt(body.ghost.mariadbRootUserPassword);
if (body.ghost?.defaultPassword) body.ghost.defaultPassword = decrypt(body.ghost.defaultPassword);
+ if (body.meiliSearch?.masterKey) body.meiliSearch.masterKey = decrypt(body.meiliSearch.masterKey);
+
if (body?.serviceSecret.length > 0) {
body.serviceSecret = body.serviceSecret.map((s) => {
s.value = decrypt(s.value);
@@ -165,6 +168,15 @@ export async function configureServiceType({ id, type }) {
}
}
});
+ } else if (type === 'meilisearch') {
+ const masterKey = encrypt(generatePassword(32));
+ await prisma.service.update({
+ where: { id },
+ data: {
+ type,
+ meiliSearch: { create: { masterKey } }
+ }
+ });
}
}
export async function setServiceVersion({ id, version }) {
@@ -191,6 +203,9 @@ export async function updateService({ id, fqdn, name }) {
export async function updateLanguageToolService({ id, fqdn, name }) {
return await prisma.service.update({ where: { id }, data: { fqdn, name } });
}
+export async function updateMeiliSearchService({ 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 } });
}
@@ -214,6 +229,7 @@ export async function updateGhostService({ id, fqdn, name, mariadbDatabase }) {
}
export async function removeService({ id }) {
+ await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
await prisma.ghost.deleteMany({ where: { serviceId: id } });
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
await prisma.minio.deleteMany({ where: { serviceId: id } });
diff --git a/src/routes/services/[id]/_Services/_MeiliSearch.svelte b/src/routes/services/[id]/_Services/_MeiliSearch.svelte
new file mode 100644
index 000000000..ba0b360dd
--- /dev/null
+++ b/src/routes/services/[id]/_Services/_MeiliSearch.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/src/routes/services/[id]/_Services/_Services.svelte b/src/routes/services/[id]/_Services/_Services.svelte
index f92741116..0d1e942a1 100644
--- a/src/routes/services/[id]/_Services/_Services.svelte
+++ b/src/routes/services/[id]/_Services/_Services.svelte
@@ -11,6 +11,7 @@
import { errorNotification } from '$lib/form';
import { toast } from '@zerodevx/svelte-toast';
import Ghost from './_Ghost.svelte';
+ import MeiliSearch from './_MeiliSearch.svelte';
import MinIo from './_MinIO.svelte';
import PlausibleAnalytics from './_PlausibleAnalytics.svelte';
import VsCodeServer from './_VSCodeServer.svelte';
@@ -145,6 +146,8 @@
{:else if service.type === 'ghost'}
+ {:else if service.type === 'meilisearch'}
+
{/if}
diff --git a/src/routes/services/[id]/configuration/type.svelte b/src/routes/services/[id]/configuration/type.svelte
index bf8575065..4ff9189b5 100644
--- a/src/routes/services/[id]/configuration/type.svelte
+++ b/src/routes/services/[id]/configuration/type.svelte
@@ -41,6 +41,7 @@
import N8n from '$lib/components/svg/services/N8n.svelte';
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
import Ghost from '$lib/components/svg/services/Ghost.svelte';
+ import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
@@ -86,6 +87,8 @@
{:else if type.name === 'ghost'}
+ {:else if type.name === 'meilisearch'}
+
{/if}{type.fancyName}
diff --git a/src/routes/services/[id]/languagetool/index.json.ts b/src/routes/services/[id]/languagetool/index.json.ts
index e33112fe8..c253112b9 100644
--- a/src/routes/services/[id]/languagetool/index.json.ts
+++ b/src/routes/services/[id]/languagetool/index.json.ts
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
if (fqdn) fqdn = fqdn.toLowerCase();
try {
- await db.updateLanguageToolService({ id, fqdn, name });
+ await db.updateMeiliSearchService({ id, fqdn, name });
return { status: 201 };
} catch (error) {
return ErrorHandler(error);
diff --git a/src/routes/services/[id]/meilisearch/index.json.ts b/src/routes/services/[id]/meilisearch/index.json.ts
new file mode 100644
index 000000000..e33112fe8
--- /dev/null
+++ b/src/routes/services/[id]/meilisearch/index.json.ts
@@ -0,0 +1,21 @@
+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.updateLanguageToolService({ id, fqdn, name });
+ return { status: 201 };
+ } catch (error) {
+ return ErrorHandler(error);
+ }
+};
diff --git a/src/routes/services/[id]/meilisearch/start.json.ts b/src/routes/services/[id]/meilisearch/start.json.ts
new file mode 100644
index 000000000..646a8b1ba
--- /dev/null
+++ b/src/routes/services/[id]/meilisearch/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 { 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 {
+ meiliSearch: { masterKey }
+ } = service;
+ 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}-datams:/data.ms`,
+ environmentVariables: {
+ MEILI_MASTER_KEY: masterKey
+ }
+ };
+
+ 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],
+ environment: config.environmentVariables,
+ restart: 'always',
+ volumes: [config.volume],
+ labels: makeLabelForServices('meilisearch')
+ }
+ },
+ 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 {
+ if (version === 'latest') {
+ await asyncExecShell(
+ `DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`
+ );
+ }
+ 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]/meilisearch/stop.json.ts b/src/routes/services/[id]/meilisearch/stop.json.ts
new file mode 100644
index 000000000..c604e1cc3
--- /dev/null
+++ b/src/routes/services/[id]/meilisearch/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/index.svelte b/src/routes/services/index.svelte
index 2b5584270..4cba9a702 100644
--- a/src/routes/services/index.svelte
+++ b/src/routes/services/index.svelte
@@ -11,6 +11,7 @@
import N8n from '$lib/components/svg/services/N8n.svelte';
import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte';
import Ghost from '$lib/components/svg/services/Ghost.svelte';
+ import MeiliSearch from '$lib/components/svg/services/MeiliSearch.svelte';
export let services;
async function newService() {
@@ -67,6 +68,8 @@
{:else if service.type === 'ghost'}
+ {:else if service.type === 'meilisearch'}
+
{/if}
{service.name}