diff --git a/src/app.d.ts b/src/app.d.ts index d69042fe8..2ebfc021b 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -8,9 +8,11 @@ declare namespace App { interface Platform {} interface Session extends SessionData {} interface Stuff { + service: any; application: any; isRunning: boolean; appId: string; + readOnly: boolean; } } diff --git a/src/lib/components/svg/services/UptimeKuma.svelte b/src/lib/components/svg/services/UptimeKuma.svelte new file mode 100644 index 000000000..e043ea54b --- /dev/null +++ b/src/lib/components/svg/services/UptimeKuma.svelte @@ -0,0 +1,159 @@ + + + diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index 67316fd06..19922cece 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -174,6 +174,15 @@ export const supportedServiceTypesAndVersions = [ ports: { main: 5678 } + }, + { + name: 'uptimekuma', + fancyName: 'Uptime Kuma', + baseImage: 'louislam/uptime-kuma', + versions: ['latest'], + ports: { + main: 3001 + } } ]; diff --git a/src/lib/database/services.ts b/src/lib/database/services.ts index 4ed11f7fe..daec2a5da 100644 --- a/src/lib/database/services.ts +++ b/src/lib/database/services.ts @@ -126,6 +126,13 @@ export async function configureServiceType({ id, type }) { type } }); + } else if (type === 'uptimekuma') { + await prisma.service.update({ + where: { id }, + data: { + type + } + }); } } export async function setServiceVersion({ id, version }) { diff --git a/src/routes/services/[id]/configuration/type.svelte b/src/routes/services/[id]/configuration/type.svelte index af10189a2..6a56354be 100644 --- a/src/routes/services/[id]/configuration/type.svelte +++ b/src/routes/services/[id]/configuration/type.svelte @@ -39,6 +39,7 @@ 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'; + import UptimeKuma from '$lib/components/svg/services/UptimeKuma.svelte'; const { id } = $page.params; const from = $page.url.searchParams.get('from'); @@ -80,6 +81,8 @@ {:else if type.name === 'n8n'} + {:else if type.name === 'uptimekuma'} + {/if}{type.fancyName} diff --git a/src/routes/services/[id]/index.svelte b/src/routes/services/[id]/index.svelte index 6dbf5b6f6..bcc69b8a7 100644 --- a/src/routes/services/[id]/index.svelte +++ b/src/routes/services/[id]/index.svelte @@ -40,6 +40,7 @@ import { browser } from '$app/env'; 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'; export let service; export let isRunning; @@ -114,6 +115,10 @@ + {:else if service.type === 'uptimekuma'} + + + {/if} diff --git a/src/routes/services/[id]/uptimekuma/index.json.ts b/src/routes/services/[id]/uptimekuma/index.json.ts new file mode 100644 index 000000000..5ec3fa69a --- /dev/null +++ b/src/routes/services/[id]/uptimekuma/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]/uptimekuma/start.json.ts b/src/routes/services/[id]/uptimekuma/start.json.ts new file mode 100644 index 000000000..961a546c4 --- /dev/null +++ b/src/routes/services/[id]/uptimekuma/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}-uptimekuma:/app/data`, + 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('uptimekuma') + } + }, + 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]/uptimekuma/stop.json.ts b/src/routes/services/[id]/uptimekuma/stop.json.ts new file mode 100644 index 000000000..c604e1cc3 --- /dev/null +++ b/src/routes/services/[id]/uptimekuma/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); + } +};