feat: Ghost service

This commit is contained in:
Andras Bacsai 2022-03-27 22:03:21 +02:00
parent e471b11d3b
commit 2475031f88
27 changed files with 509 additions and 111 deletions

View File

@ -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");

View File

@ -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
}

View File

@ -0,0 +1,9 @@
<script lang="ts">
export let isAbsolute = false;
</script>
<img
alt="ghost logo"
class={isAbsolute ? 'w-12 absolute top-0 left-0 -m-3 -mt-5' : 'w-10 mx-auto'}
src="/ghost.png"
/>

View File

@ -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,

View File

@ -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 } });

View File

@ -104,6 +104,7 @@ export async function generateSSLCerts() {
});
for (const application of applications) {
try {
if (application.fqdn && application.destinationDockerId) {
const {
fqdn,
id,
@ -133,6 +134,7 @@ export async function generateSSLCerts() {
}
}
}
}
} catch (error) {
console.log(`Error during generateSSLCerts with ${application.fqdn}: ${error}`);
}
@ -143,13 +145,15 @@ 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 {
if (service.fqdn && service.destinationDockerId) {
const {
fqdn,
id,
@ -165,6 +169,7 @@ export async function generateSSLCerts() {
if (isHttps) ssls.push({ domain, id, isCoolify: false });
}
}
}
} catch (error) {
console.log(`Error during generateSSLCerts with ${service.fqdn}: ${error}`);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -0,0 +1,90 @@
<script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
export let readOnly;
export let service;
</script>
<div class="flex space-x-1 py-5 font-bold">
<div class="title">Ghost</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="email">Default Email Address</label>
<input
name="email"
id="email"
disabled
readonly
placeholder="Email address"
value={service.ghost.defaultEmail}
required
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="defaultPassword">Default Password</label>
<CopyPasswordField
id="defaultPassword"
isPasswordField
readonly
disabled
name="defaultPassword"
value={service.ghost.defaultPassword}
/>
</div>
<div class="flex space-x-1 py-5 font-bold">
<div class="title">MariaDB</div>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mariadbUser">Username</label>
<CopyPasswordField
name="mariadbUser"
id="mariadbUser"
value={service.ghost.mariadbUser}
readonly
disabled
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mariadbPassword">Password</label>
<CopyPasswordField
id="mariadbPassword"
isPasswordField
readonly
disabled
name="mariadbPassword"
value={service.ghost.mariadbPassword}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mariadbDatabase">Database</label>
<input
name="mariadbDatabase"
id="mariadbDatabase"
required
readonly={readOnly}
disabled={readOnly}
bind:value={service.ghost.mariadbDatabase}
placeholder="eg: ghost_db"
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mariadbRootUser">Root DB User</label>
<CopyPasswordField
id="mariadbRootUser"
isPasswordField
readonly
disabled
name="mariadbRootUser"
value={service.ghost.mariadbRootUser}
/>
</div>
<div class="grid grid-cols-2 items-center px-10">
<label for="mariadbRootUserPassword">Root DB Password</label>
<CopyPasswordField
id="mariadbRootUserPassword"
isPasswordField
readonly
disabled
name="mariadbRootUserPassword"
value={service.ghost.mariadbRootUserPassword}
/>
</div>

View File

@ -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 @@
<VsCodeServer {service} />
{:else if service.type === 'wordpress'}
<Wordpress bind:service {isRunning} {readOnly} />
{:else if service.type === 'ghost'}
<Ghost bind:service {readOnly} />
{/if}
</div>
</form>

View File

@ -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: {

View File

@ -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 @@
<N8n isAbsolute />
{:else if type.name === 'uptimekuma'}
<UptimeKuma isAbsolute />
{:else if type.name === 'ghost'}
<Ghost isAbsolute />
{/if}{type.fancyName}
</button>
</form>

View File

@ -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);
}
};

View File

@ -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);
}
};

View File

@ -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);
}
};

View File

@ -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}`

View File

@ -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 @@
<a href="https://github.com/louislam/uptime-kuma" target="_blank">
<UptimeKuma />
</a>
{:else if service.type === 'ghost'}
<a href="https://ghost.org" target="_blank">
<Ghost />
</a>
{/if}
</div>
</div>

View File

@ -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

View File

@ -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 });

View File

@ -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

View File

@ -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

View File

@ -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
};

View File

@ -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

View File

@ -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 {
if (version === 'latest') {
await asyncExecShell(
`DOCKER_HOST=${host} docker volume create ${config.volume.split(':')[0]}`
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`
);
} catch (error) {
console.log(error);
}
try {
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
return {
status: 200

View File

@ -61,22 +61,16 @@ 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);
if (version === 'latest') {
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
}
try {
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
return {
status: 200
@ -84,7 +78,4 @@ export const post: RequestHandler = async (event) => {
} catch (error) {
return ErrorHandler(error);
}
} catch (error) {
return ErrorHandler(error);
}
};

View File

@ -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 {
if (version === 'latest') {
await asyncExecShell(
`DOCKER_HOST=${host} docker volume create ${config.mysql.volume.split(':')[0]}`
`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`
);
await asyncExecShell(
`DOCKER_HOST=${host} docker volume create ${config.wordpress.volume.split(':')[0]}`
);
} catch (error) {
console.log(error);
}
try {
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
return {
status: 200

BIN
static/ghost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB