diff --git a/prisma/migrations/20220327180323_ghost/migration.sql b/prisma/migrations/20220327180323_ghost/migration.sql new file mode 100644 index 000000000..3c1cec36f --- /dev/null +++ b/prisma/migrations/20220327180323_ghost/migration.sql @@ -0,0 +1,19 @@ +-- CreateTable +CREATE TABLE "Ghost" ( + "id" TEXT NOT NULL PRIMARY KEY, + "defaultEmail" TEXT NOT NULL, + "defaultPassword" TEXT NOT NULL, + "mariadbUser" TEXT NOT NULL, + "mariadbPassword" TEXT NOT NULL, + "mariadbRootUser" TEXT NOT NULL, + "mariadbRootUserPassword" TEXT NOT NULL, + "mariadbDatabase" TEXT, + "mariadbPublicPort" INTEGER, + "serviceId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Ghost_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "Ghost_serviceId_key" ON "Ghost"("serviceId"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 95310d2aa..a849ae36f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -278,6 +278,7 @@ model Service { minio Minio? vscodeserver Vscodeserver? wordpress Wordpress? + ghost Ghost? serviceSecret ServiceSecret[] } @@ -332,3 +333,19 @@ model Wordpress { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + +model Ghost { + id String @id @default(cuid()) + defaultEmail String + defaultPassword String + mariadbUser String + mariadbPassword String + mariadbRootUser String + mariadbRootUserPassword String + mariadbDatabase String? + mariadbPublicPort Int? + 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/Ghost.svelte b/src/lib/components/svg/services/Ghost.svelte new file mode 100644 index 000000000..2e77177da --- /dev/null +++ b/src/lib/components/svg/services/Ghost.svelte @@ -0,0 +1,9 @@ + + +ghost logo diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index 19922cece..e89105f05 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -107,6 +107,7 @@ export const supportedServiceTypesAndVersions = [ name: 'plausibleanalytics', fancyName: 'Plausible Analytics', baseImage: 'plausible/analytics', + images: ['bitnami/postgresql:13.2.0', 'yandex/clickhouse-server:21.3.2.5'], versions: ['latest'], ports: { main: 8000 @@ -143,6 +144,7 @@ export const supportedServiceTypesAndVersions = [ name: 'wordpress', fancyName: 'Wordpress', baseImage: 'wordpress', + images: ['bitnami/mysql:5.7'], versions: ['latest', 'php8.1', 'php8.0', 'php7.4', 'php7.3'], ports: { main: 80 @@ -183,6 +185,16 @@ export const supportedServiceTypesAndVersions = [ ports: { main: 3001 } + }, + { + name: 'ghost', + fancyName: 'Ghost', + baseImage: 'bitnami/ghost', + images: ['bitnami/mariadb'], + versions: ['latest'], + ports: { + main: 2368 + } } ]; @@ -207,6 +219,13 @@ export function getServiceImage(type) { } return ''; } +export function getServiceImages(type) { + const found = supportedServiceTypesAndVersions.find((t) => t.name === type); + if (found) { + return found.images; + } + return []; +} export function generateDatabaseConfiguration(database) { const { id, diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts index daec2a5da..a5c2ee9ea 100644 --- a/src/lib/database/services.ts +++ b/src/lib/database/services.ts @@ -1,3 +1,4 @@ +import { asyncExecShell, getEngine } from '$lib/common'; import { decrypt, encrypt } from '$lib/crypto'; import cuid from 'cuid'; import { generatePassword } from '.'; @@ -20,6 +21,7 @@ export async function getService({ id, teamId }) { minio: true, vscodeserver: true, wordpress: true, + ghost: true, serviceSecret: true } }); @@ -43,12 +45,18 @@ export async function getService({ id, teamId }) { if (body.wordpress?.mysqlRootUserPassword) body.wordpress.mysqlRootUserPassword = decrypt(body.wordpress.mysqlRootUserPassword); + if (body.ghost?.mariadbPassword) body.ghost.mariadbPassword = decrypt(body.ghost.mariadbPassword); + if (body.ghost?.mariadbRootUserPassword) + body.ghost.mariadbRootUserPassword = decrypt(body.ghost.mariadbRootUserPassword); + if (body.ghost?.defaultPassword) body.ghost.defaultPassword = decrypt(body.ghost.defaultPassword); + if (body?.serviceSecret.length > 0) { body.serviceSecret = body.serviceSecret.map((s) => { s.value = decrypt(s.value); return s; }); } + return { ...body }; } @@ -133,6 +141,30 @@ export async function configureServiceType({ id, type }) { type } }); + } else if (type === 'ghost') { + const defaultEmail = `${cuid()}@coolify.io`; + const defaultPassword = encrypt(generatePassword()); + const mariadbUser = cuid(); + const mariadbPassword = encrypt(generatePassword()); + const mariadbRootUser = cuid(); + const mariadbRootUserPassword = encrypt(generatePassword()); + + await prisma.service.update({ + where: { id }, + data: { + type, + ghost: { + create: { + defaultEmail, + defaultPassword, + mariadbUser, + mariadbPassword, + mariadbRootUser, + mariadbRootUserPassword + } + } + } + }); } } export async function setServiceVersion({ id, version }) { @@ -174,8 +206,15 @@ export async function updateWordpress({ id, fqdn, name, mysqlDatabase, extraConf export async function updateMinioService({ id, publicPort }) { return await prisma.minio.update({ where: { serviceId: id }, data: { publicPort } }); } +export async function updateGhostService({ id, fqdn, name, mariadbDatabase }) { + return await prisma.service.update({ + where: { id }, + data: { fqdn, name, ghost: { update: { mariadbDatabase } } } + }); +} export async function removeService({ id }) { + await prisma.ghost.deleteMany({ where: { serviceId: id } }); await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } }); await prisma.minio.deleteMany({ where: { serviceId: id } }); await prisma.vscodeserver.deleteMany({ where: { serviceId: id } }); diff --git a/src/lib/letsencrypt/index.ts b/src/lib/letsencrypt/index.ts index e85f539de..eb74aa252 100644 --- a/src/lib/letsencrypt/index.ts +++ b/src/lib/letsencrypt/index.ts @@ -104,32 +104,34 @@ export async function generateSSLCerts() { }); for (const application of applications) { try { - const { - fqdn, - id, - destinationDocker: { engine, network }, - settings: { previews } - } = application; - const isRunning = await checkContainer(engine, id); - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - if (isRunning) { - if (isHttps) ssls.push({ domain, id, isCoolify: false }); - } - if (previews) { - const host = getEngine(engine); - const { stdout } = await asyncExecShell( - `DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` - ); - const containers = stdout - .trim() - .split('\n') - .filter((a) => a) - .map((c) => c.replace(/"/g, '')); - if (containers.length > 0) { - for (const container of containers) { - let previewDomain = `${container.split('-')[1]}.${domain}`; - if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); + if (application.fqdn && application.destinationDockerId) { + const { + fqdn, + id, + destinationDocker: { engine, network }, + settings: { previews } + } = application; + const isRunning = await checkContainer(engine, id); + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } + if (previews) { + const host = getEngine(engine); + const { stdout } = await asyncExecShell( + `DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` + ); + const containers = stdout + .trim() + .split('\n') + .filter((a) => a) + .map((c) => c.replace(/"/g, '')); + if (containers.length > 0) { + for (const container of containers) { + let previewDomain = `${container.split('-')[1]}.${domain}`; + if (isHttps) ssls.push({ domain: previewDomain, id, isCoolify: false }); + } } } } @@ -143,26 +145,29 @@ export async function generateSSLCerts() { minio: true, plausibleAnalytics: true, vscodeserver: true, - wordpress: true + wordpress: true, + ghost: true }, orderBy: { createdAt: 'desc' } }); for (const service of services) { try { - const { - fqdn, - id, - type, - destinationDocker: { engine } - } = service; - const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); - if (found) { - const domain = getDomain(fqdn); - const isHttps = fqdn.startsWith('https://'); - const isRunning = await checkContainer(engine, id); - if (isRunning) { - if (isHttps) ssls.push({ domain, id, isCoolify: false }); + if (service.fqdn && service.destinationDockerId) { + const { + fqdn, + id, + type, + destinationDocker: { engine } + } = service; + const found = db.supportedServiceTypesAndVersions.find((a) => a.name === type); + if (found) { + const domain = getDomain(fqdn); + const isHttps = fqdn.startsWith('https://'); + const isRunning = await checkContainer(engine, id); + if (isRunning) { + if (isHttps) ssls.push({ domain, id, isCoolify: false }); + } } } } catch (error) { diff --git a/src/routes/destinations/[id]/_LocalDocker.svelte b/src/routes/destinations/[id]/_LocalDocker.svelte index bf4d8447a..329d23c2f 100644 --- a/src/routes/destinations/[id]/_LocalDocker.svelte +++ b/src/routes/destinations/[id]/_LocalDocker.svelte @@ -103,7 +103,7 @@ } async function forceRestartProxy() { const sure = confirm( - 'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.' + 'Are you sure you want to restart the proxy? Everything will be reconfigured in ~10 secs.' ); if (sure) { try { diff --git a/src/routes/destinations/[id]/_RemoteDocker.svelte b/src/routes/destinations/[id]/_RemoteDocker.svelte index 9c13cf465..4de1b82ce 100644 --- a/src/routes/destinations/[id]/_RemoteDocker.svelte +++ b/src/routes/destinations/[id]/_RemoteDocker.svelte @@ -106,7 +106,7 @@ } async function forceRestartProxy() { const sure = confirm( - 'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.' + 'Are you sure you want to restart the proxy? Everything will be reconfigured in ~10 secs.' ); if (sure) { try { diff --git a/src/routes/services/[id]/_Services/_Ghost.svelte b/src/routes/services/[id]/_Services/_Ghost.svelte new file mode 100644 index 000000000..8a6154ac9 --- /dev/null +++ b/src/routes/services/[id]/_Services/_Ghost.svelte @@ -0,0 +1,90 @@ + + +
+
Ghost
+
+
+ + +
+
+ + +
+
+
MariaDB
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
diff --git a/src/routes/services/[id]/_Services/_Services.svelte b/src/routes/services/[id]/_Services/_Services.svelte index 896953cc4..6a54d9b98 100644 --- a/src/routes/services/[id]/_Services/_Services.svelte +++ b/src/routes/services/[id]/_Services/_Services.svelte @@ -10,6 +10,7 @@ import Setting from '$lib/components/Setting.svelte'; import { errorNotification } from '$lib/form'; import { toast } from '@zerodevx/svelte-toast'; + import Ghost from './_Ghost.svelte'; import MinIo from './_MinIO.svelte'; import PlausibleAnalytics from './_PlausibleAnalytics.svelte'; import VsCodeServer from './_VSCodeServer.svelte'; @@ -142,6 +143,8 @@ {:else if service.type === 'wordpress'} + {:else if service.type === 'ghost'} + {/if} diff --git a/src/routes/services/[id]/__layout.svelte b/src/routes/services/[id]/__layout.svelte index fffce0007..362f89786 100644 --- a/src/routes/services/[id]/__layout.svelte +++ b/src/routes/services/[id]/__layout.svelte @@ -35,6 +35,7 @@ } if (service.plausibleAnalytics?.email && service.plausibleAnalytics.username) readOnly = true; if (service.wordpress?.mysqlDatabase) readOnly = true; + if (service.ghost?.mariadbDatabase && service.ghost.mariadbDatabase) readOnly = true; return { props: { diff --git a/src/routes/services/[id]/configuration/type.svelte b/src/routes/services/[id]/configuration/type.svelte index 6a56354be..bf8575065 100644 --- a/src/routes/services/[id]/configuration/type.svelte +++ b/src/routes/services/[id]/configuration/type.svelte @@ -40,6 +40,7 @@ import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte'; 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'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -83,6 +84,8 @@ {:else if type.name === 'uptimekuma'} + {:else if type.name === 'ghost'} + {/if}{type.fancyName} diff --git a/src/routes/services/[id]/ghost/index.json.ts b/src/routes/services/[id]/ghost/index.json.ts new file mode 100644 index 000000000..81ca8ade1 --- /dev/null +++ b/src/routes/services/[id]/ghost/index.json.ts @@ -0,0 +1,23 @@ +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, + ghost: { mariadbDatabase } + } = await event.request.json(); + if (fqdn) fqdn = fqdn.toLowerCase(); + try { + await db.updateGhostService({ id, fqdn, name, mariadbDatabase }); + return { status: 201 }; + } catch (error) { + return ErrorHandler(error); + } +}; diff --git a/src/routes/services/[id]/ghost/start.json.ts b/src/routes/services/[id]/ghost/start.json.ts new file mode 100644 index 000000000..f1f2250bd --- /dev/null +++ b/src/routes/services/[id]/ghost/start.json.ts @@ -0,0 +1,134 @@ +import { + asyncExecShell, + createDirectories, + getDomain, + 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, + fqdn, + ghost: { + defaultEmail, + defaultPassword, + mariadbRootUser, + mariadbRootUserPassword, + mariadbDatabase, + mariadbPassword, + mariadbUser + } + } = service; + const network = destinationDockerId && destinationDocker.network; + const host = getEngine(destinationDocker.engine); + + const { workdir } = await createDirectories({ repository: type, buildId: id }); + const image = getServiceImage(type); + const domain = getDomain(fqdn); + const config = { + ghost: { + image: `${image}:${version}`, + volume: `${id}-ghost:/bitnami/ghost`, + environmentVariables: { + GHOST_HOST: domain, + GHOST_EMAIL: defaultEmail, + GHOST_PASSWORD: defaultPassword, + GHOST_DATABASE_HOST: `${id}-mariadb`, + GHOST_DATABASE_USER: mariadbUser, + GHOST_DATABASE_PASSWORD: mariadbPassword, + GHOST_DATABASE_NAME: mariadbDatabase, + GHOST_DATABASE_PORT_NUMBER: 3306 + } + }, + mariadb: { + image: `bitnami/mariadb:latest`, + volume: `${id}-mariadb:/bitnami/mariadb`, + environmentVariables: { + MARIADB_USER: mariadbUser, + MARIADB_PASSWORD: mariadbPassword, + MARIADB_DATABASE: mariadbDatabase, + MARIADB_ROOT_USER: mariadbRootUser, + MARIADB_ROOT_PASSWORD: mariadbRootUserPassword + } + } + }; + if (serviceSecret.length > 0) { + serviceSecret.forEach((secret) => { + config.ghost.environmentVariables[secret.name] = secret.value; + }); + } + const composeFile = { + version: '3.8', + services: { + [id]: { + container_name: id, + image: config.ghost.image, + networks: [network], + volumes: [config.ghost.volume], + environment: config.ghost.environmentVariables, + restart: 'always', + labels: makeLabelForServices('ghost'), + depends_on: [`${id}-mariadb`] + }, + [`${id}-mariadb`]: { + container_name: `${id}-mariadb`, + image: config.mariadb.image, + networks: [network], + volumes: [config.mariadb.volume], + environment: config.mariadb.environmentVariables, + restart: 'always' + } + }, + networks: { + [network]: { + external: true + } + }, + volumes: { + [config.ghost.volume.split(':')[0]]: { + name: config.ghost.volume.split(':')[0] + }, + [config.mariadb.volume.split(':')[0]]: { + name: config.mariadb.volume.split(':')[0] + } + } + }; + console.log(JSON.stringify(composeFile.volumes)); + 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]/ghost/stop.json.ts b/src/routes/services/[id]/ghost/stop.json.ts new file mode 100644 index 000000000..478f5b946 --- /dev/null +++ b/src/routes/services/[id]/ghost/stop.json.ts @@ -0,0 +1,39 @@ +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 { + let found = await checkContainer(engine, id); + if (found) { + await removeDestinationDocker({ id, engine }); + } + found = await checkContainer(engine, `${id}-mariadb`); + if (found) { + await removeDestinationDocker({ id: `${id}-mariadb`, engine }); + } + } catch (error) { + console.error(error); + } + } + + return { + status: 200 + }; + } catch (error) { + return ErrorHandler(error); + } +}; diff --git a/src/routes/services/[id]/index.json.ts b/src/routes/services/[id]/index.json.ts index d712c1c72..676ff6405 100644 --- a/src/routes/services/[id]/index.json.ts +++ b/src/routes/services/[id]/index.json.ts @@ -4,7 +4,8 @@ import { generateDatabaseConfiguration, getServiceImage, getVersions, - ErrorHandler + ErrorHandler, + getServiceImages } from '$lib/database'; import { dockerInstance } from '$lib/docker'; import type { RequestHandler } from '@sveltejs/kit'; @@ -23,7 +24,13 @@ export const get: RequestHandler = async (event) => { const host = getEngine(destinationDocker.engine); const docker = dockerInstance({ destinationDocker }); const baseImage = getServiceImage(type); + const images = getServiceImages(type); docker.engine.pull(`${baseImage}:${version}`); + if (images?.length > 0) { + for (const image of images) { + docker.engine.pull(`${image}:latest`); + } + } try { const { stdout } = await asyncExecShell( `DOCKER_HOST=${host} docker inspect --format '{{json .State}}' ${id}` diff --git a/src/routes/services/[id]/index.svelte b/src/routes/services/[id]/index.svelte index bcc69b8a7..ada5c0d9e 100644 --- a/src/routes/services/[id]/index.svelte +++ b/src/routes/services/[id]/index.svelte @@ -41,6 +41,7 @@ import LanguageTool from '$lib/components/svg/services/LanguageTool.svelte'; 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'; export let service; export let isRunning; @@ -119,6 +120,10 @@ + {:else if service.type === 'ghost'} + + + {/if} diff --git a/src/routes/services/[id]/languagetool/start.json.ts b/src/routes/services/[id]/languagetool/start.json.ts index 4666fea79..a6a81d475 100644 --- a/src/routes/services/[id]/languagetool/start.json.ts +++ b/src/routes/services/[id]/languagetool/start.json.ts @@ -41,7 +41,7 @@ export const post: RequestHandler = async (event) => { networks: [network], environment: config.environmentVariables, restart: 'always', - volumes: [`${id}-ngrams:/ngrams`], + volumes: [config.volume], labels: makeLabelForServices('languagetool') } }, @@ -51,20 +51,20 @@ export const post: RequestHandler = async (event) => { } }, volumes: { - [`${id}-ngrams`]: { - external: true + [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 volume create ${id}-ngrams`); - } catch (error) { - console.log(error); - } 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 diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index 99665dfca..bcffd9e8f 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -76,19 +76,13 @@ export const post: RequestHandler = async (event) => { }, volumes: { [config.volume.split(':')[0]]: { - external: true + 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 volume create ${config.volume.split(':')[0]}` - ); - } catch (error) { - console.log(error); - } + try { await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); await db.updateMinioService({ id, publicPort }); diff --git a/src/routes/services/[id]/n8n/start.json.ts b/src/routes/services/[id]/n8n/start.json.ts index 6c9f63b76..4a04917c7 100644 --- a/src/routes/services/[id]/n8n/start.json.ts +++ b/src/routes/services/[id]/n8n/start.json.ts @@ -59,6 +59,11 @@ export const post: RequestHandler = async (event) => { 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 diff --git a/src/routes/services/[id]/nocodb/start.json.ts b/src/routes/services/[id]/nocodb/start.json.ts index 1088bf817..ebc59ce97 100644 --- a/src/routes/services/[id]/nocodb/start.json.ts +++ b/src/routes/services/[id]/nocodb/start.json.ts @@ -52,6 +52,11 @@ export const post: RequestHandler = async (event) => { 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 diff --git a/src/routes/services/[id]/plausibleanalytics/start.json.ts b/src/routes/services/[id]/plausibleanalytics/start.json.ts index 4b635264b..836bd0178 100644 --- a/src/routes/services/[id]/plausibleanalytics/start.json.ts +++ b/src/routes/services/[id]/plausibleanalytics/start.json.ts @@ -158,29 +158,21 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`; }, volumes: { [config.postgresql.volume.split(':')[0]]: { - external: true + name: config.postgresql.volume.split(':')[0] }, [config.clickhouse.volume.split(':')[0]]: { - external: true + name: config.clickhouse.volume.split(':')[0] } } }; const composeFileDestination = `${workdir}/docker-compose.yaml`; await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); - try { - await asyncExecShell( - `DOCKER_HOST=${host} docker volume create ${config.postgresql.volume.split(':')[0]}` - ); - await asyncExecShell( - `DOCKER_HOST=${host} docker volume create ${config.clickhouse.volume.split(':')[0]}` - ); - } catch (error) { - console.log(error); + if (version === 'latest') { + await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`); } await asyncExecShell( `DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up --build -d` ); - return { status: 200 }; diff --git a/src/routes/services/[id]/uptimekuma/start.json.ts b/src/routes/services/[id]/uptimekuma/start.json.ts index 961a546c4..845873bb0 100644 --- a/src/routes/services/[id]/uptimekuma/start.json.ts +++ b/src/routes/services/[id]/uptimekuma/start.json.ts @@ -59,6 +59,11 @@ export const post: RequestHandler = async (event) => { 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 diff --git a/src/routes/services/[id]/vaultwarden/start.json.ts b/src/routes/services/[id]/vaultwarden/start.json.ts index 703a71473..f977adda3 100644 --- a/src/routes/services/[id]/vaultwarden/start.json.ts +++ b/src/routes/services/[id]/vaultwarden/start.json.ts @@ -52,20 +52,18 @@ export const post: RequestHandler = async (event) => { }, volumes: { [config.volume.split(':')[0]]: { - external: true + 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 volume create ${config.volume.split(':')[0]}` - ); - } catch (error) { - console.log(error); - } - 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 diff --git a/src/routes/services/[id]/vscodeserver/start.json.ts b/src/routes/services/[id]/vscodeserver/start.json.ts index be43cb2a7..6f51fcf2a 100644 --- a/src/routes/services/[id]/vscodeserver/start.json.ts +++ b/src/routes/services/[id]/vscodeserver/start.json.ts @@ -61,29 +61,20 @@ export const post: RequestHandler = async (event) => { }, volumes: { [config.volume.split(':')[0]]: { - external: true + 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 volume create ${config.volume.split(':')[0]}` - ); - } catch (error) { - console.log(error); - } - - try { - await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`); - return { - status: 200 - }; - } catch (error) { - return ErrorHandler(error); + 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); } diff --git a/src/routes/services/[id]/wordpress/start.json.ts b/src/routes/services/[id]/wordpress/start.json.ts index d1685d8b1..98e7d6aab 100644 --- a/src/routes/services/[id]/wordpress/start.json.ts +++ b/src/routes/services/[id]/wordpress/start.json.ts @@ -72,6 +72,7 @@ export const post: RequestHandler = async (event) => { container_name: id, image: config.wordpress.image, environment: config.wordpress.environmentVariables, + volumes: [config.wordpress.volume], networks: [network], restart: 'always', depends_on: [`${id}-mysql`], @@ -80,6 +81,7 @@ export const post: RequestHandler = async (event) => { [`${id}-mysql`]: { container_name: `${id}-mysql`, image: config.mysql.image, + volumes: [config.mysql.volume], environment: config.mysql.environmentVariables, networks: [network], restart: 'always' @@ -91,29 +93,22 @@ export const post: RequestHandler = async (event) => { } }, volumes: { - [config.mysql.volume.split(':')[0]]: { - external: true - }, [config.wordpress.volume.split(':')[0]]: { - external: true + name: config.wordpress.volume.split(':')[0] + }, + [config.mysql.volume.split(':')[0]]: { + name: config.mysql.volume.split(':')[0] } } }; const composeFileDestination = `${workdir}/docker-compose.yaml`; await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); - - try { - await asyncExecShell( - `DOCKER_HOST=${host} docker volume create ${config.mysql.volume.split(':')[0]}` - ); - await asyncExecShell( - `DOCKER_HOST=${host} docker volume create ${config.wordpress.volume.split(':')[0]}` - ); - } catch (error) { - console.log(error); - } - 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 diff --git a/static/ghost.png b/static/ghost.png new file mode 100644 index 000000000..f4e300654 Binary files /dev/null and b/static/ghost.png differ