From c55505af6c1f3719458b1d8fae20eb896ca5e669 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 23:08:27 +0200 Subject: [PATCH] feat: MeiliSearch service --- .../20220402210645_meilisearch/migration.sql | 12 +++ prisma/schema.prisma | 10 +++ .../svg/services/MeiliSearch.svelte | 45 ++++++++++ src/lib/database/common.ts | 10 +++ src/lib/database/services.ts | 18 +++- .../[id]/_Services/_MeiliSearch.svelte | 19 +++++ .../services/[id]/_Services/_Services.svelte | 3 + .../services/[id]/configuration/type.svelte | 3 + .../services/[id]/languagetool/index.json.ts | 2 +- .../services/[id]/meilisearch/index.json.ts | 21 +++++ .../services/[id]/meilisearch/start.json.ts | 83 +++++++++++++++++++ .../services/[id]/meilisearch/stop.json.ts | 35 ++++++++ src/routes/services/index.svelte | 3 + 13 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20220402210645_meilisearch/migration.sql create mode 100644 src/lib/components/svg/services/MeiliSearch.svelte create mode 100644 src/routes/services/[id]/_Services/_MeiliSearch.svelte create mode 100644 src/routes/services/[id]/meilisearch/index.json.ts create mode 100644 src/routes/services/[id]/meilisearch/start.json.ts create mode 100644 src/routes/services/[id]/meilisearch/stop.json.ts 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 @@ + + +
+
MeiliSearch
+
+
+ + +
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}