From dde2772e52688bd8d582229fbbbf498d6f970111 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 7 Sep 2022 20:14:49 +0000 Subject: [PATCH 01/15] fix: dnsServer formatting --- apps/api/src/lib/common.ts | 6 +++--- apps/api/src/routes/api/v1/settings/handlers.ts | 2 +- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index f644780a3..c0dd7d96a 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -21,7 +21,7 @@ import { scheduler } from './scheduler'; import { supportedServiceTypesAndVersions } from './services/supportedVersions'; import { includeServices } from './services/common'; -export const version = '3.9.3'; +export const version = '3.9.4'; export const isDev = process.env.NODE_ENV === 'development'; const algorithm = 'aes-256-ctr'; @@ -193,7 +193,7 @@ export async function isDNSValid(hostname: any, domain: string): Promise { const { isIP } = await import('is-ip'); const { DNSServers } = await listSettings(); if (DNSServers) { - dns.setServers([DNSServers]); + dns.setServers([...DNSServers.split(',')]); } let resolves = []; try { @@ -302,7 +302,7 @@ export async function checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }): P const { DNSServers } = await listSettings(); if (DNSServers) { - dns.setServers([DNSServers]); + dns.setServers([...DNSServers.split(',')]); } let resolves = []; diff --git a/apps/api/src/routes/api/v1/settings/handlers.ts b/apps/api/src/routes/api/v1/settings/handlers.ts index e9d91dd8c..831ce606e 100644 --- a/apps/api/src/routes/api/v1/settings/handlers.ts +++ b/apps/api/src/routes/api/v1/settings/handlers.ts @@ -58,7 +58,7 @@ export async function deleteDomain(request: FastifyRequest, reply: const { fqdn } = request.body const { DNSServers } = await listSettings(); if (DNSServers) { - dns.setServers([DNSServers]); + dns.setServers([...DNSServers.split(',')]); } let ip; try { diff --git a/package.json b/package.json index e005b8a9d..bb5daaa1e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "3.9.3", + "version": "3.9.4", "license": "Apache-2.0", "repository": "github:coollabsio/coolify", "scripts": { From ab07adb14fa8772cc4540a9f63bf5522244554fc Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 7 Sep 2022 20:32:15 +0000 Subject: [PATCH 02/15] fix: Settings for service --- apps/api/src/routes/api/v1/services/handlers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routes/api/v1/services/handlers.ts b/apps/api/src/routes/api/v1/services/handlers.ts index c6858a7d7..cd1c996bf 100644 --- a/apps/api/src/routes/api/v1/services/handlers.ts +++ b/apps/api/src/routes/api/v1/services/handlers.ts @@ -1,7 +1,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify'; import fs from 'fs/promises'; import yaml from 'js-yaml'; -import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort } from '../../../../lib/common'; +import { prisma, uniqueName, asyncExecShell, getServiceFromDB, getContainerUsage, isDomainConfigured, saveUpdateableFields, fixType, decrypt, encrypt, ComposeFile, getFreePublicPort, getDomain, errorHandler, generatePassword, isDev, stopTcpHttpProxy, executeDockerCmd, checkDomainsIsValidInDNS, checkExposedPort, listSettings } from '../../../../lib/common'; import { day } from '../../../../lib/dayjs'; import { checkContainer, isContainerExited } from '../../../../lib/docker'; import cuid from 'cuid'; @@ -70,6 +70,7 @@ export async function getService(request: FastifyRequest) { throw { status: 404, message: 'Service not found.' } } return { + settings: await listSettings(), service } } catch ({ status, message }) { From f2a915700c9e6be39d21ac2d33cce90b924d16e2 Mon Sep 17 00:00:00 2001 From: David Mydlarz Date: Thu, 8 Sep 2022 13:33:18 +0900 Subject: [PATCH 03/15] fix type located in postCreateCommand in devcontainers.json --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 25e97c31a..eda081b6c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -26,7 +26,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [3000, 3001], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "cp apps/api/.env.example pps/api/.env && pnpm install && pnpm db:push && pnpm db:seed", + "postCreateCommand": "cp apps/api/.env.example apps/api/.env && pnpm install && pnpm db:push && pnpm db:seed", // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "node", "features": { From a1a973a873b69892e4dd91d620dab3d94b70e60e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 09:32:50 +0200 Subject: [PATCH 04/15] fix: change to execa from utils --- apps/api/src/lib/common.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 48f1f97b4..1c598dcbf 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -500,6 +500,7 @@ export async function createRemoteEngineConfiguration(id: string) { return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config)) } export async function executeDockerCmd({ debug, buildId, applicationId, dockerId, command }: { debug?: boolean, buildId?: string, applicationId?: string, dockerId: string, command: string }): Promise { + const { execaCommand } = await import('execa') let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } }) if (remoteEngine) { await createRemoteEngineConfiguration(dockerId) @@ -515,10 +516,7 @@ export async function executeDockerCmd({ debug, buildId, applicationId, dockerId if (command.startsWith(`docker build --progress plain`)) { return await asyncExecShellStream({ debug, buildId, applicationId, command, engine }); } - return await asyncExecShell( - `DOCKER_BUILDKIT=1 DOCKER_HOST="${engine}" ${command}` - ); - + return await execaCommand(command, { env: { DOCKER_BUILDKIT: "1", DOCKER_HOST: engine }, shell: true }) } export async function startTraefikProxy(id: string): Promise { const { engine, network, remoteEngine, remoteIpAddress } = await prisma.destinationDocker.findUnique({ where: { id } }) From 7350524456dba40634c615f4d8a02b09e34b14ad Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 10:30:13 +0200 Subject: [PATCH 05/15] ui: dashboard updates --- apps/ui/src/routes/__layout.svelte | 158 ----- apps/ui/src/routes/index.svelte | 1036 +++++++++++++--------------- 2 files changed, 495 insertions(+), 699 deletions(-) diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index 38fb61673..3feade818 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -159,156 +159,6 @@ -
@@ -408,14 +258,6 @@
-Dashboard -Applications -Git Sources -Destinations -Databases -Services IAM Settings diff --git a/apps/ui/src/routes/index.svelte b/apps/ui/src/routes/index.svelte index d9c646a83..5b5a70ea8 100644 --- a/apps/ui/src/routes/index.svelte +++ b/apps/ui/src/routes/index.svelte @@ -27,31 +27,7 @@ export let gitSources: any; export let destinations: any; - let filtered = { - applications: applications.filter( - (application: any) => application.teams[0].id === $appSession.teamId - ), - otherApplications: applications.filter( - (application: any) => application.teams[0].id !== $appSession.teamId - ), - databases: databases.filter((database: any) => database.teams[0].id === $appSession.teamId), - otherDatabases: databases.filter( - (database: any) => database.teams[0].id !== $appSession.teamId - ), - services: services.filter((service: any) => service.teams[0].id === $appSession.teamId), - otherServices: services.filter((service: any) => service.teams[0].id !== $appSession.teamId), - settings, - gitSources: gitSources.filter((gitSource: any) => gitSource.teams[0].id === $appSession.teamId), - otherGitSources: gitSources.filter( - (gitSource: any) => gitSource.teams[0].id !== $appSession.teamId - ), - destinations: destinations.filter( - (destination: any) => destination.teams[0].id === $appSession.teamId - ), - otherDestinations: destinations.filter( - (destination: any) => destination.teams[0].id !== $appSession.teamId - ) - }; + let filtered: any = setInitials(); import { get, post } from '$lib/api'; import Usage from '$lib/components/Usage.svelte'; import { t } from '$lib/translations'; @@ -68,6 +44,50 @@ let search = ''; let status: any = {}; + function setInitials(onlyOthers: boolean = false) { + return { + applications: + !onlyOthers && + applications.filter((application: any) => application.teams[0].id === $appSession.teamId), + otherApplications: applications.filter( + (application: any) => application.teams[0].id !== $appSession.teamId + ), + databases: + !onlyOthers && + databases.filter((database: any) => database.teams[0].id === $appSession.teamId), + otherDatabases: databases.filter( + (database: any) => database.teams[0].id !== $appSession.teamId + ), + services: + !onlyOthers && + services.filter((service: any) => service.teams[0].id === $appSession.teamId), + otherServices: services.filter((service: any) => service.teams[0].id !== $appSession.teamId), + gitSources: + !onlyOthers && + gitSources.filter((gitSource: any) => gitSource.teams[0].id === $appSession.teamId), + otherGitSources: gitSources.filter( + (gitSource: any) => gitSource.teams[0].id !== $appSession.teamId + ), + destinations: + !onlyOthers && + destinations.filter((destination: any) => destination.teams[0].id === $appSession.teamId), + otherDestinations: destinations.filter( + (destination: any) => destination.teams[0].id !== $appSession.teamId + ) + }; + } + function clearFiltered() { + filtered.applications = []; + filtered.otherApplications = []; + filtered.databases = []; + filtered.otherDatabases = []; + filtered.services = []; + filtered.otherServices = []; + filtered.gitSources = []; + filtered.otherGitSources = []; + filtered.destinations = []; + filtered.otherDestinations = []; + } function getRndInteger(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } @@ -92,264 +112,165 @@ isRunning = response.isRunning; } if (isRunning) { - status[id] = 'Running'; - return 'Running'; + status[id] = 'running'; + return 'running'; } else { - status[id] = 'Stopped'; - return 'Stopped'; + status[id] = 'stopped'; + return 'stopped'; } } catch (error) { - status[id] = 'Error'; - return 'Error'; + status[id] = 'error'; + return 'error'; } finally { numberOfGetStatus--; } } - + function filterState(state: string) { + clearFiltered(); + filtered.applications = applications.filter((application: any) => { + if (status[application.id] === state && application.teams[0].id === $appSession.teamId) + return application; + }); + filtered.otherApplications = applications.filter((application: any) => { + if (status[application.id] === state && application.teams[0].id !== $appSession.teamId) + return application; + }); + filtered.databases = databases.filter((database: any) => { + if (status[database.id] === state && database.teams[0].id === $appSession.teamId) + return database; + }); + filtered.otherDatabases = databases.filter((database: any) => { + if (status[database.id] === state && database.teams[0].id !== $appSession.teamId) + return database; + }); + filtered.services = services.filter((service: any) => { + if (status[service.id] === state && service.teams[0].id === $appSession.teamId) + return service; + }); + filtered.otherServices = services.filter((service: any) => { + if (status[service.id] === state && service.teams[0].id !== $appSession.teamId) + return service; + }); + } + function filterSpecific(type: any) { + clearFiltered(); + const otherType = 'other' + type[0].toUpperCase() + type.substring(1); + filtered[type] = eval(type).filter( + (resource: any) => resource.teams[0].id === $appSession.teamId + ); + filtered[otherType] = eval(type).filter( + (resource: any) => resource.teams[0].id !== $appSession.teamId + ); + } + function applicationFilters(application: any) { + return ( + (application.name && application.name.toLowerCase().includes(search.toLowerCase())) || + (application.fqdn && application.fqdn.toLowerCase().includes(search.toLowerCase())) || + (application.repository && + application.repository.toLowerCase().includes(search.toLowerCase())) || + (application.buildpack && + application.buildpack.toLowerCase().includes(search.toLowerCase())) || + (application.branch && application.branch.toLowerCase().includes(search.toLowerCase())) || + (application.destinationDockerId && + application.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) || + ('bot'.includes(search) && application.settings.isBot) + ); + } + function databaseFilters(database: any) { + return ( + (database.name && database.name.toLowerCase().includes(search.toLowerCase())) || + (database.type && database.type.toLowerCase().includes(search.toLowerCase())) || + (database.version && database.version.toLowerCase().includes(search.toLowerCase())) || + (database.destinationDockerId && + database.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) + ); + } + function serviceFilters(service: any) { + return ( + (service.name && service.name.toLowerCase().includes(search.toLowerCase())) || + (service.type && service.type.toLowerCase().includes(search.toLowerCase())) || + (service.version && service.version.toLowerCase().includes(search.toLowerCase())) || + (service.destinationDockerId && + service.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) + ); + } + function gitSourceFilters(source: any) { + return ( + (source.name && source.name.toLowerCase().includes(search.toLowerCase())) || + (source.type && source.type.toLowerCase().includes(search.toLowerCase())) || + (source.htmlUrl && source.htmlUrl.toLowerCase().includes(search.toLowerCase())) || + (source.apiUrl && source.apiUrl.toLowerCase().includes(search.toLowerCase())) + ); + } + function destinationFilters(destination: any) { + return ( + (destination.name && destination.name.toLowerCase().includes(search.toLowerCase())) || + (destination.type && destination.type.toLowerCase().includes(search.toLowerCase())) + ); + } function doSearch(bang?: string) { if (bang || bang === '') search = bang; if (search) { + filtered = setInitials(); if (search.startsWith('!')) { if (search === '!running') { - filtered.applications = applications.filter((application: any) => { - if ( - status[application.id] === 'Running' && - application.teams[0].id === $appSession.teamId - ) - return application; - }); - filtered.otherApplications = applications.filter((application: any) => { - if ( - status[application.id] === 'Running' && - application.teams[0].id !== $appSession.teamId - ) - return application; - }); - filtered.databases = databases.filter((database: any) => { - if (status[database.id] === 'Running' && database.teams[0].id === $appSession.teamId) - return database; - }); - filtered.otherDatabases = databases.filter((database: any) => { - if (status[database.id] === 'Running' && database.teams[0].id !== $appSession.teamId) - return database; - }); - filtered.services = services.filter((service: any) => { - if (status[service.id] === 'Running' && service.teams[0].id === $appSession.teamId) - return service; - }); - filtered.otherServices = services.filter((service: any) => { - if (status[service.id] === 'Running' && service.teams[0].id !== $appSession.teamId) - return service; - }); - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterState('running'); } else if (search === '!stopped') { - filtered.applications = applications.filter((application: any) => { - if ( - status[application.id] === 'Stopped' && - application.teams[0].id === $appSession.teamId - ) - return application; - }); - filtered.otherApplications = applications.filter((application: any) => { - if ( - status[application.id] === 'Stopped' && - application.teams[0].id !== $appSession.teamId - ) - return application; - }); - filtered.databases = databases.filter((database: any) => { - if (status[database.id] === 'Stopped' && database.teams[0].id === $appSession.teamId) - return database; - }); - filtered.otherDatabases = databases.filter((database: any) => { - if (status[database.id] === 'Stopped' && database.teams[0].id !== $appSession.teamId) - return database; - }); - filtered.services = services.filter((service: any) => { - if (status[service.id] === 'Stopped' && service.teams[0].id === $appSession.teamId) - return service; - }); - filtered.otherServices = services.filter((service: any) => { - if (status[service.id] === 'Stopped' && service.teams[0].id !== $appSession.teamId) - return service; - }); - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterState('stopped'); } else if (search === '!error') { - filtered.applications = applications.filter((application: any) => { - if ( - status[application.id] === 'Error' && - application.teams[0].id === $appSession.teamId - ) - return application; - }); - filtered.otherApplications = applications.filter((application: any) => { - if ( - status[application.id] === 'Error' && - application.teams[0].id !== $appSession.teamId - ) - return application; - }); - filtered.databases = databases.filter((database: any) => { - if (status[database.id] === 'Error' && database.teams[0].id === $appSession.teamId) - return database; - }); - filtered.otherDatabases = databases.filter((database: any) => { - if (status[database.id] === 'Error' && database.teams[0].id !== $appSession.teamId) - return database; - }); - filtered.services = services.filter((service: any) => { - if (status[service.id] === 'Error' && service.teams[0].id === $appSession.teamId) - return service; - }); - filtered.otherServices = services.filter((service: any) => { - if (status[service.id] === 'Error' && service.teams[0].id !== $appSession.teamId) - return service; - }); - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterState('error'); } else if (search === '!app') { - filtered.applications = applications.filter( - (application: any) => application.teams[0].id === $appSession.teamId - ); - filtered.databases = []; - filtered.otherDatabases = []; - filtered.services = []; - filtered.otherServices = []; - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterSpecific('applications'); } else if (search === '!db') { - filtered.applications = []; - filtered.otherApplications = []; - filtered.databases = databases.filter( - (database: any) => database.teams[0].id === $appSession.teamId - ); - filtered.services = []; - filtered.otherServices = []; - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterSpecific('databases'); } else if (search === '!service') { - filtered.applications = []; - filtered.otherApplications = []; - filtered.databases = []; - filtered.otherDatabases = []; - filtered.services = services.filter( - (service: any) => service.teams[0].id === $appSession.teamId - ); - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = []; - filtered.otherDestinations = []; + filterSpecific('services'); } else if (search === '!git') { - filtered.applications = []; - filtered.otherApplications = []; - filtered.databases = []; - filtered.otherDatabases = []; - filtered.services = []; - filtered.otherServices = []; - filtered.gitSources = gitSources.filter( - (source: any) => source.teams[0].id === $appSession.teamId - ); - filtered.destinations = []; - filtered.otherDestinations = []; + filterSpecific('gitSources'); } else if (search === '!destination') { - filtered.applications = []; - filtered.otherApplications = []; - filtered.databases = []; - filtered.otherDatabases = []; - filtered.services = []; - filtered.otherServices = []; - filtered.gitSources = []; - filtered.otherGitSources = []; - filtered.destinations = destinations.filter( - (destination: any) => destination.teams[0].id === $appSession.teamId - ); + filterSpecific('destinations'); } else if (search === '!bot') { + clearFiltered(); filtered.applications = applications.filter((application: any) => { return application.settings.isBot; }); filtered.otherApplications = applications.filter((application: any) => { return application.settings.isBot && application.teams[0].id !== $appSession.teamId; }); - filtered.databases = []; - filtered.otherDatabases = []; - filtered.services = []; - filtered.otherServices = []; - filtered.gitSources = []; - filtered.otherGitSources = []; } else if (search === '!notmine') { - filtered.applications = []; - filtered.databases = []; - filtered.services = []; - filtered.gitSources = []; - filtered.destinations = []; + clearFiltered(); + filtered = setInitials(true); } } else { - filtered.applications = applications.filter((application: any) => { - return ( - (application.name && application.name.toLowerCase().includes(search.toLowerCase())) || - (application.fqdn && application.fqdn.toLowerCase().includes(search.toLowerCase())) || - (application.repository && - application.repository.toLowerCase().includes(search.toLowerCase())) || - (application.buildpack && - application.buildpack.toLowerCase().includes(search.toLowerCase())) || - (application.branch && - application.branch.toLowerCase().includes(search.toLowerCase())) || - (application.destinationDockerId && - application.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) || - ('bot'.includes(search) && application.settings.isBot) - ); - }); - filtered.databases = databases.filter((database: any) => { - return ( - (database.name && database.name.toLowerCase().includes(search.toLowerCase())) || - (database.type && database.type.toLowerCase().includes(search.toLowerCase())) || - (database.version && database.version.toLowerCase().includes(search.toLowerCase())) || - (database.destinationDockerId && - database.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) - ); - }); - filtered.services = services.filter((service: any) => { - return ( - (service.name && service.name.toLowerCase().includes(search.toLowerCase())) || - (service.type && service.type.toLowerCase().includes(search.toLowerCase())) || - (service.version && service.version.toLowerCase().includes(search.toLowerCase())) || - (service.destinationDockerId && - service.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) - ); - }); - filtered.gitSources = gitSources.filter((source: any) => { - return ( - (source.name && source.name.toLowerCase().includes(search.toLowerCase())) || - (source.type && source.type.toLowerCase().includes(search.toLowerCase())) || - (source.htmlUrl && source.htmlUrl.toLowerCase().includes(search.toLowerCase())) || - (source.apiUrl && source.apiUrl.toLowerCase().includes(search.toLowerCase())) - ); - }); - filtered.destinations = destinations.filter((destination: any) => { - return ( - (destination.name && destination.name.toLowerCase().includes(search.toLowerCase())) || - (destination.type && destination.type.toLowerCase().includes(search.toLowerCase())) - ); - }); + filtered.applications = filtered.applications.filter((application: any) => + applicationFilters(application) + ); + filtered.otherApplications = filtered.otherApplications.filter((application: any) => + applicationFilters(application) + ); + filtered.databases = filtered.databases.filter((database: any) => + databaseFilters(database) + ); + filtered.otherDatabases = filtered.otherDatabases.filter((database: any) => + databaseFilters(database) + ); + filtered.services = filtered.services.filter((service: any) => serviceFilters(service)); + filtered.otherServices = filtered.otherServices.filter((service: any) => + serviceFilters(service) + ); + filtered.gitSources = filtered.gitSources.filter((source: any) => gitSourceFilters(source)); + filtered.otherGitSources = filtered.otherGitSources.filter((source: any) => + gitSourceFilters(source) + ); + filtered.destinations = filtered.destinations.filter((destination: any) => + destinationFilters(destination) + ); + filtered.otherDestinations = filtered.otherDestinations.filter((destination: any) => + destinationFilters(destination) + ); } } else { - filtered.applications = applications; - filtered.databases = databases; - filtered.services = services; - filtered.gitSources = gitSources; - filtered.destinations = destinations; + filtered = setInitials(); } } @@ -367,96 +288,98 @@ {#if applications.length !== 0 || destinations.length !== 0 || databases.length !== 0 || services.length !== 0 || gitSources.length !== 0 || destinations.length !== 0}
-
+
doSearch('')} + > + + + +
doSearch()} />
{/if} {#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0} -
+

Applications

{/if} {#if filtered.applications.length > 0 && applications.length > 0}
-
@@ -467,7 +390,7 @@ {#await getStatus(application)} {:then status} - {#if status === 'Running'} + {#if status === 'running'} {:else} @@ -493,6 +416,9 @@ {#if application.destinationDocker?.name}
{application.destinationDocker.name}
{/if} + {#if application.teams.length > 0 && application.teams[0].name} +
{application.teams[0].name}
+ {/if}
@@ -560,17 +486,17 @@

Other Teams

{/if} -
- {#if filtered.otherApplications.length > 0} + {#if filtered.otherApplications.length > 0} + - {#if (filtered.services.length > 0 && services.length > 0) || filtered.services.length > 0} +
+ {/if} + {#if (filtered.services.length > 0 && services.length > 0) || filtered.otherServices.length > 0}

Services

@@ -666,7 +595,7 @@ {#await getStatus(service)} {:then status} - {#if status === 'Running'} + {#if status === 'running'} {:else} @@ -685,6 +614,9 @@ {#if service.destinationDocker?.name}
{service.destinationDocker.name}
{/if} + {#if service.teams.length > 0 && service.teams[0].name} +
{service.teams[0].name}
+ {/if}
{#if service.fqdn} @@ -716,74 +648,77 @@

Nothing here.

{/if}
- {#if filtered.otherServices.length > 0} - {#if filtered.services.length > 0} -
- {/if} -
-

Other Teams

-
+ {/if} + {#if filtered.otherServices.length > 0} + {#if filtered.services.length > 0} +
{/if} - {#if filtered.otherServices.length > 0} -
- {#each filtered.otherServices as service} - -
- {#await getStatus(service)} - - {:then status} - {#if status === 'Running'} - - {:else} - - {/if} - {/await} -
- - {/if} - {#if (filtered.databases.length > 0 && databases.length > 0) || filtered.databases.length > 0} + {#if (filtered.databases.length > 0 && databases.length > 0) || filtered.otherDatabases.length > 0}

Databases

@@ -800,7 +735,7 @@ {#await getStatus(database)} {:then status} - {#if status === 'Running'} + {#if status === 'running'} {:else} @@ -820,6 +755,9 @@ {#if database.destinationDocker?.name}
{database.destinationDocker.name}
{/if} + {#if database.teams.length > 0 && database.teams[0].name} +
{database.teams[0].name}
+ {/if}
@@ -854,78 +792,81 @@

Nothing here.

{/if}
- {#if filtered.otherDatabases.length > 0} - {#if databases.length > 0} -
- {/if} -
-

Other Teams

-
+ {/if} + {#if filtered.otherDatabases.length > 0} + {#if filtered.databases.length > 0} +
{/if} - {#if filtered.otherDatabases.length > 0} -
- {#each filtered.otherDatabases as database} - -
- {#await getStatus(database)} - - {:then status} - {#if status === 'Running'} - - {:else} - - {/if} - {/await} -
- - - {#if filtered.otherGitSources.length > 0} - {#if gitSources.length > 0} -
- {/if} -
-

Other Teams

-
+ {/if} + {#if filtered.otherGitSources.length > 0} + {#if filtered.gitSources.length > 0} +
{/if} - {#if filtered.otherGitSources.length > 0} -
- {#each filtered.otherGitSources as source} - -
-
-
- {#if source?.type === 'gitlab'} - - +

Other Teams

+
+ {/if} + {#if filtered.otherGitSources.length > 0} +
+ {#each filtered.otherGitSources as source} + + {/if} - {#if (filtered.destinations.length > 0 && destinations.length > 0) || filtered.destinations.length > 0} + {#if (filtered.destinations.length > 0 && destinations.length > 0) || filtered.otherDestinations.length > 0}

Destinations

@@ -1124,6 +1072,9 @@ {#if destination.remoteEngine && !destination.sshKeyId}

SSH key missing

{/if} + {#if destination.teams.length > 0 && destination.teams[0].name} +
{destination.teams[0].name}
+ {/if}
@@ -1134,82 +1085,85 @@

Nothing here.

{/if}
- {#if filtered.otherDestinations.length > 0} - {#if destinations.length > 0} -
- {/if} -
-

Other Teams

-
+ {/if} + {#if filtered.otherDestinations.length > 0} + {#if filtered.destinations.length > 0} +
{/if} - {#if filtered.otherDestinations.length > 0} -
- {#each filtered.otherDestinations as destination} - -
-
- {/if} {#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && search} From 3832d332598ca748eabd448b29603a063b8b10b9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 10:45:12 +0200 Subject: [PATCH 06/15] fix: save search input --- apps/ui/src/lib/store.ts | 2 + apps/ui/src/routes/index.svelte | 99 ++++++++++++++++----------------- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/apps/ui/src/lib/store.ts b/apps/ui/src/lib/store.ts index 5355d22c8..29f48a048 100644 --- a/apps/ui/src/lib/store.ts +++ b/apps/ui/src/lib/store.ts @@ -26,6 +26,8 @@ interface AddToast { message: string, timeout?: number | undefined } + +export const search: any = writable('') export const loginEmail: Writable = writable() export const appSession: Writable = writable({ isRegistrationEnabled: false, diff --git a/apps/ui/src/routes/index.svelte b/apps/ui/src/routes/index.svelte index 5b5a70ea8..537057d09 100644 --- a/apps/ui/src/routes/index.svelte +++ b/apps/ui/src/routes/index.svelte @@ -32,7 +32,7 @@ import Usage from '$lib/components/Usage.svelte'; import { t } from '$lib/translations'; import { asyncSleep } from '$lib/common'; - import { appSession } from '$lib/store'; + import { appSession, search } from '$lib/store'; import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte'; import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte'; @@ -41,9 +41,8 @@ import NewResource from './_NewResource.svelte'; let numberOfGetStatus = 0; - let search = ''; let status: any = {}; - + doSearch(); function setInitials(onlyOthers: boolean = false) { return { applications: @@ -164,72 +163,72 @@ } function applicationFilters(application: any) { return ( - (application.name && application.name.toLowerCase().includes(search.toLowerCase())) || - (application.fqdn && application.fqdn.toLowerCase().includes(search.toLowerCase())) || + (application.name && application.name.toLowerCase().includes($search.toLowerCase())) || + (application.fqdn && application.fqdn.toLowerCase().includes($search.toLowerCase())) || (application.repository && - application.repository.toLowerCase().includes(search.toLowerCase())) || + application.repository.toLowerCase().includes($search.toLowerCase())) || (application.buildpack && - application.buildpack.toLowerCase().includes(search.toLowerCase())) || - (application.branch && application.branch.toLowerCase().includes(search.toLowerCase())) || + application.buildpack.toLowerCase().includes($search.toLowerCase())) || + (application.branch && application.branch.toLowerCase().includes($search.toLowerCase())) || (application.destinationDockerId && - application.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) || - ('bot'.includes(search) && application.settings.isBot) + application.destinationDocker.name.toLowerCase().includes($search.toLowerCase())) || + ('bot'.includes($search) && application.settings.isBot) ); } function databaseFilters(database: any) { return ( - (database.name && database.name.toLowerCase().includes(search.toLowerCase())) || - (database.type && database.type.toLowerCase().includes(search.toLowerCase())) || - (database.version && database.version.toLowerCase().includes(search.toLowerCase())) || + (database.name && database.name.toLowerCase().includes($search.toLowerCase())) || + (database.type && database.type.toLowerCase().includes($search.toLowerCase())) || + (database.version && database.version.toLowerCase().includes($search.toLowerCase())) || (database.destinationDockerId && - database.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) + database.destinationDocker.name.toLowerCase().includes($search.toLowerCase())) ); } function serviceFilters(service: any) { return ( - (service.name && service.name.toLowerCase().includes(search.toLowerCase())) || - (service.type && service.type.toLowerCase().includes(search.toLowerCase())) || - (service.version && service.version.toLowerCase().includes(search.toLowerCase())) || + (service.name && service.name.toLowerCase().includes($search.toLowerCase())) || + (service.type && service.type.toLowerCase().includes($search.toLowerCase())) || + (service.version && service.version.toLowerCase().includes($search.toLowerCase())) || (service.destinationDockerId && - service.destinationDocker.name.toLowerCase().includes(search.toLowerCase())) + service.destinationDocker.name.toLowerCase().includes($search.toLowerCase())) ); } function gitSourceFilters(source: any) { return ( - (source.name && source.name.toLowerCase().includes(search.toLowerCase())) || - (source.type && source.type.toLowerCase().includes(search.toLowerCase())) || - (source.htmlUrl && source.htmlUrl.toLowerCase().includes(search.toLowerCase())) || - (source.apiUrl && source.apiUrl.toLowerCase().includes(search.toLowerCase())) + (source.name && source.name.toLowerCase().includes($search.toLowerCase())) || + (source.type && source.type.toLowerCase().includes($search.toLowerCase())) || + (source.htmlUrl && source.htmlUrl.toLowerCase().includes($search.toLowerCase())) || + (source.apiUrl && source.apiUrl.toLowerCase().includes($search.toLowerCase())) ); } function destinationFilters(destination: any) { return ( - (destination.name && destination.name.toLowerCase().includes(search.toLowerCase())) || - (destination.type && destination.type.toLowerCase().includes(search.toLowerCase())) + (destination.name && destination.name.toLowerCase().includes($search.toLowerCase())) || + (destination.type && destination.type.toLowerCase().includes($search.toLowerCase())) ); } function doSearch(bang?: string) { - if (bang || bang === '') search = bang; - if (search) { + if (bang || bang === '') $search = bang; + if ($search) { filtered = setInitials(); - if (search.startsWith('!')) { - if (search === '!running') { + if ($search.startsWith('!')) { + if ($search === '!running') { filterState('running'); - } else if (search === '!stopped') { + } else if ($search === '!stopped') { filterState('stopped'); - } else if (search === '!error') { + } else if ($search === '!error') { filterState('error'); - } else if (search === '!app') { + } else if ($search === '!app') { filterSpecific('applications'); - } else if (search === '!db') { + } else if ($search === '!db') { filterSpecific('databases'); - } else if (search === '!service') { + } else if ($search === '!service') { filterSpecific('services'); - } else if (search === '!git') { + } else if ($search === '!git') { filterSpecific('gitSources'); - } else if (search === '!destination') { + } else if ($search === '!destination') { filterSpecific('destinations'); - } else if (search === '!bot') { + } else if ($search === '!bot') { clearFiltered(); filtered.applications = applications.filter((application: any) => { return application.settings.isBot; @@ -237,7 +236,7 @@ filtered.otherApplications = applications.filter((application: any) => { return application.settings.isBot && application.teams[0].id !== $appSession.teamId; }); - } else if (search === '!notmine') { + } else if ($search === '!notmine') { clearFiltered(); filtered = setInitials(true); } @@ -313,59 +312,59 @@ type="text" placeholder="Search: You can search for names, domains, types, database types, version, servers etc..." class="w-full input input-bordered input-primary" - bind:value={search} + bind:value={$search} on:input={() => doSearch()} />
{/if} - {#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && search} + {#if filtered.applications.length === 0 && filtered.destinations.length === 0 && filtered.databases.length === 0 && filtered.services.length === 0 && filtered.gitSources.length === 0 && filtered.destinations.length === 0 && $search}

- Nothing found with {search}. + Nothing found with {$search}.

{/if} From e36fda3ff111414d3454397d867faa5f3b5b33b2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 10:54:24 +0200 Subject: [PATCH 07/15] fix: ispublic status on databases --- apps/ui/src/lib/store.ts | 3 ++- .../[id]/_Databases/_Databases.svelte | 19 +++++++++---------- .../src/routes/databases/[id]/__layout.svelte | 2 ++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/ui/src/lib/store.ts b/apps/ui/src/lib/store.ts index 29f48a048..ec841a2e5 100644 --- a/apps/ui/src/lib/store.ts +++ b/apps/ui/src/lib/store.ts @@ -86,7 +86,8 @@ export const status: Writable = writable({ isRunning: false, isExited: false, loading: false, - initialLoading: true + initialLoading: true, + isPublic: false } }); diff --git a/apps/ui/src/routes/databases/[id]/_Databases/_Databases.svelte b/apps/ui/src/routes/databases/[id]/_Databases/_Databases.svelte index 61972edfe..14a88b00b 100644 --- a/apps/ui/src/routes/databases/[id]/_Databases/_Databases.svelte +++ b/apps/ui/src/routes/databases/[id]/_Databases/_Databases.svelte @@ -24,7 +24,6 @@ let loading = false; let publicLoading = false; - let isPublic = database.settings.isPublic || false; let appendOnly = database.settings.appendOnly; let databaseDefault: any; @@ -52,12 +51,12 @@ return `${database.type}://${ databaseDbUser ? databaseDbUser + ':' : '' }${databaseDbUserPassword}@${ - isPublic + $status.database.isPublic ? database.destinationDocker.remoteEngine ? database.destinationDocker.remoteIpAddress : $appSession.ipv4 : database.id - }:${isPublic ? database.publicPort : privatePort}/${databaseDefault}`; + }:${$status.database.isPublic ? database.publicPort : privatePort}/${databaseDefault}`; } async function changeSettings(name: any) { @@ -66,11 +65,11 @@ } publicLoading = true; let data = { - isPublic, + isPublic: $status.database.isPublic, appendOnly }; if (name === 'isPublic') { - data.isPublic = !isPublic; + data.isPublic = !$status.database.isPublic; } if (name === 'appendOnly') { data.appendOnly = !appendOnly; @@ -80,9 +79,9 @@ isPublic: data.isPublic, appendOnly: data.appendOnly }); - isPublic = data.isPublic; + $status.database.isPublic = data.isPublic; appendOnly = data.appendOnly; - if (isPublic) { + if ($status.database.isPublic) { database.publicPort = publicPort; } } catch (error) { @@ -191,7 +190,7 @@ readonly disabled name="publicPort" - value={publicLoading ? 'Loading...' : isPublic ? database.publicPort : privatePort} + value={publicLoading ? 'Loading...' : $status.database.isPublic ? database.publicPort : privatePort} />
@@ -215,7 +214,7 @@
- {#if database.settings.isPublic} + {#if database.settings?.isPublic}
Not version not configured {/if} {#if database.destinationDocker?.name} -
{database.destinationDocker.name}
+
{database.destinationDocker?.name}
{/if} - {#if database.teams.length > 0 && database.teams[0].name} -
{database.teams[0].name}
+ {#if database.teams.length > 0 && database.teams[0]?.name} +
{database.teams[0]?.name}
{/if}
- {#if database.settings.isPublic} + {#if database.settings?.isPublic}

{source.name}

- {#if source.teams.length > 0 && source.teams[0].name} -
{source.teams[0].name}
+ {#if source.teams.length > 0 && source.teams[0]?.name} +
{source.teams[0]?.name}
{/if}
@@ -992,8 +992,8 @@

{source.name}

- {#if source.teams.length > 0 && source.teams[0].name} -
{source.teams[0].name}
+ {#if source.teams.length > 0 && source.teams[0]?.name} +
{source.teams[0]?.name}
{/if}
@@ -1071,8 +1071,8 @@ {#if destination.remoteEngine && !destination.sshKeyId}

SSH key missing

{/if} - {#if destination.teams.length > 0 && destination.teams[0].name} -
{destination.teams[0].name}
+ {#if destination.teams.length > 0 && destination.teams[0]?.name} +
{destination.teams[0]?.name}
{/if}
@@ -1153,8 +1153,8 @@ {#if destination.remoteEngine && !destination.sshKeyId}

SSH key missing

{/if} - {#if destination.teams.length > 0 && destination.teams[0].name} -
{destination.teams[0].name}
+ {#if destination.teams.length > 0 && destination.teams[0]?.name} +
{destination.teams[0]?.name}
{/if}
From 071077200b3001dcbd05a8ddf236d4cdb561a34d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 11:57:15 +0200 Subject: [PATCH 10/15] ui: fix tooltip --- apps/ui/src/lib/components/UpdateAvailable.svelte | 8 +++++--- apps/ui/src/routes/__layout.svelte | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/ui/src/lib/components/UpdateAvailable.svelte b/apps/ui/src/lib/components/UpdateAvailable.svelte index db98d8699..55f5bc8e3 100644 --- a/apps/ui/src/lib/components/UpdateAvailable.svelte +++ b/apps/ui/src/lib/components/UpdateAvailable.svelte @@ -4,6 +4,7 @@ import { addToast, appSession, features } from '$lib/store'; import { asyncSleep, errorNotification } from '$lib/common'; import { onMount } from 'svelte'; + import Tooltip from './Tooltip.svelte'; let isUpdateAvailable = false; let updateStatus: any = { @@ -75,14 +76,14 @@ }); -
+
{#if $appSession.teamId === '0'} {#if isUpdateAvailable} + New Version Available! {/if} {/if}
diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index 3feade818..b9d346da5 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -132,9 +132,9 @@ White labeled logo
{/if} -
+ + Dashboard
From 372c0ed457e1c61b1d2f62ceb83d74794cf60359 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 12:04:08 +0200 Subject: [PATCH 11/15] fix: glitchtip env to pyhton boolean --- apps/api/src/lib/services/handlers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/lib/services/handlers.ts b/apps/api/src/lib/services/handlers.ts index cd4d58d2b..7f13f7219 100644 --- a/apps/api/src/lib/services/handlers.ts +++ b/apps/api/src/lib/services/handlers.ts @@ -1979,8 +1979,8 @@ async function startGlitchTipService(request: FastifyRequest) EMAIL_PORT: emailSmtpPort, EMAIL_HOST_USER: emailSmtpUser, EMAIL_HOST_PASSWORD: emailSmtpPassword, - EMAIL_USE_TLS: emailSmtpUseTls, - EMAIL_USE_SSL: emailSmtpUseSsl, + EMAIL_USE_TLS: emailSmtpUseTls ? 'True' : 'False', + EMAIL_USE_SSL: emailSmtpUseSsl ? 'True' : 'False', EMAIL_BACKEND: emailBackend, MAILGUN_API_KEY: mailgunApiKey, SENDGRID_API_KEY: sendgridApiKey, From 0e13e3bd816eab6d33fe89fa08071bf9a7557a4b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 14:42:04 +0200 Subject: [PATCH 12/15] feat: new servers view --- apps/api/src/lib/common.ts | 19 ++- apps/api/src/routes/api/v1/handlers.ts | 26 +--- apps/api/src/routes/api/v1/index.ts | 6 +- .../api/src/routes/api/v1/servers/handlers.ts | 117 ++++++++++++++++++ apps/api/src/routes/api/v1/servers/index.ts | 14 +++ apps/api/src/routes/api/v1/servers/types.ts | 27 ++++ apps/ui/src/lib/components/Usage.svelte | 67 +++++++--- apps/ui/src/routes/__layout.svelte | 31 ++++- apps/ui/src/routes/servers/index.svelte | 47 +++++++ 9 files changed, 304 insertions(+), 50 deletions(-) create mode 100644 apps/api/src/routes/api/v1/servers/handlers.ts create mode 100644 apps/api/src/routes/api/v1/servers/index.ts create mode 100644 apps/api/src/routes/api/v1/servers/types.ts create mode 100644 apps/ui/src/routes/servers/index.svelte diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 4e843c900..50a8dd4db 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -499,9 +499,26 @@ export async function createRemoteEngineConfiguration(id: string) { } return await fs.writeFile(`${homedir}/.ssh/config`, sshConfig.stringify(config)) } +export async function executeSSHCmd({ dockerId, command }) { + const { execaCommand } = await import('execa') + let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } }) + if (remoteEngine) { + await createRemoteEngineConfiguration(dockerId) + engine = `ssh://${remoteIpAddress}` + } else { + engine = 'unix:///var/run/docker.sock' + } + if (process.env.CODESANDBOX_HOST) { + if (command.startsWith('docker compose')) { + command = command.replace(/docker compose/gi, 'docker-compose') + } + } + command = `ssh ${remoteIpAddress} ${command}` + return await execaCommand(command) +} export async function executeDockerCmd({ debug, buildId, applicationId, dockerId, command }: { debug?: boolean, buildId?: string, applicationId?: string, dockerId: string, command: string }): Promise { const { execaCommand } = await import('execa') - let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } }) + let { remoteEngine, remoteIpAddress, engine, remoteUser } = await prisma.destinationDocker.findUnique({ where: { id: dockerId } }) if (remoteEngine) { await createRemoteEngineConfiguration(dockerId) engine = `ssh://${remoteIpAddress}` diff --git a/apps/api/src/routes/api/v1/handlers.ts b/apps/api/src/routes/api/v1/handlers.ts index 09dfbea48..cdb115af3 100644 --- a/apps/api/src/routes/api/v1/handlers.ts +++ b/apps/api/src/routes/api/v1/handlers.ts @@ -1,5 +1,4 @@ -import os from 'node:os'; -import osu from 'node-os-utils'; + import axios from 'axios'; import { compareVersions } from 'compare-versions'; import cuid from 'cuid'; @@ -15,9 +14,10 @@ export async function hashPassword(password: string): Promise { return bcrypt.hash(password, saltRounds); } -export async function cleanupManually() { +export async function cleanupManually(request: FastifyRequest) { try { - const destination = await prisma.destinationDocker.findFirst({ where: { engine: '/var/run/docker.sock' } }) + const { serverId } = request.body; + const destination = await prisma.destinationDocker.findUnique({ where: { id: serverId } }) await cleanupDockerStorage(destination.id, true, true) return {} } catch ({ status, message }) { @@ -86,25 +86,7 @@ export async function restartCoolify(request: FastifyRequest) { return errorHandler({ status, message }) } } -export async function showUsage() { - try { - return { - usage: { - uptime: os.uptime(), - memory: await osu.mem.info(), - cpu: { - load: os.loadavg(), - usage: await osu.cpu.usage(), - count: os.cpus().length - }, - disk: await osu.drive.info('/') - } - }; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} export async function showDashboard(request: FastifyRequest) { try { const userId = request.user.userId; diff --git a/apps/api/src/routes/api/v1/index.ts b/apps/api/src/routes/api/v1/index.ts index 8f9f821f8..52310998d 100644 --- a/apps/api/src/routes/api/v1/index.ts +++ b/apps/api/src/routes/api/v1/index.ts @@ -43,17 +43,13 @@ const root: FastifyPluginAsync = async (fastify): Promise => { onRequest: [fastify.authenticate] }, async (request) => await showDashboard(request)); - fastify.get('/usage', { - onRequest: [fastify.authenticate] - }, async () => await showUsage()); - fastify.post('/internal/restart', { onRequest: [fastify.authenticate] }, async (request) => await restartCoolify(request)); fastify.post('/internal/cleanup', { onRequest: [fastify.authenticate] - }, async () => await cleanupManually()); + }, async (request) => await cleanupManually(request)); }; export default root; diff --git a/apps/api/src/routes/api/v1/servers/handlers.ts b/apps/api/src/routes/api/v1/servers/handlers.ts new file mode 100644 index 000000000..9174d9bf0 --- /dev/null +++ b/apps/api/src/routes/api/v1/servers/handlers.ts @@ -0,0 +1,117 @@ +import type { FastifyRequest } from 'fastify'; +import { errorHandler, executeDockerCmd, prisma, createRemoteEngineConfiguration, executeSSHCmd } from '../../../../lib/common'; +import os from 'node:os'; +import osu from 'node-os-utils'; + + +export async function listServers(request: FastifyRequest) { + try { + const userId = request.user.userId; + const teamId = request.user.teamId; + const remoteServers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] }) + return { + servers: remoteServers + } + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} +const mappingTable = [ + ['K total memory', 'totalMemoryKB'], + ['K used memory', 'usedMemoryKB'], + ['K active memory', 'activeMemoryKB'], + ['K inactive memory', 'inactiveMemoryKB'], + ['K free memory', 'freeMemoryKB'], + ['K buffer memory', 'bufferMemoryKB'], + ['K swap cache', 'swapCacheKB'], + ['K total swap', 'totalSwapKB'], + ['K used swap', 'usedSwapKB'], + ['K free swap', 'freeSwapKB'], + ['non-nice user cpu ticks', 'nonNiceUserCpuTicks'], + ['nice user cpu ticks', 'niceUserCpuTicks'], + ['system cpu ticks', 'systemCpuTicks'], + ['idle cpu ticks', 'idleCpuTicks'], + ['IO-wait cpu ticks', 'ioWaitCpuTicks'], + ['IRQ cpu ticks', 'irqCpuTicks'], + ['softirq cpu ticks', 'softIrqCpuTicks'], + ['stolen cpu ticks', 'stolenCpuTicks'], + ['pages paged in', 'pagesPagedIn'], + ['pages paged out', 'pagesPagedOut'], + ['pages swapped in', 'pagesSwappedIn'], + ['pages swapped out', 'pagesSwappedOut'], + ['interrupts', 'interrupts'], + ['CPU context switches', 'cpuContextSwitches'], + ['boot time', 'bootTime'], + ['forks', 'forks'] +]; +function parseFromText(text) { + var data = {}; + var lines = text.split(/\r?\n/); + for (const line of lines) { + for (const [key, value] of mappingTable) { + if (line.indexOf(key) >= 0) { + const values = line.match(/[0-9]+/)[0]; + data[value] = parseInt(values, 10); + } + } + } + return data; +} +export async function showUsage(request: FastifyRequest) { + const { id } = request.params; + let { remoteEngine } = request.query + remoteEngine = remoteEngine === 'true' ? true : false + if (remoteEngine) { + const { stdout: stats } = await executeSSHCmd({ dockerId: id, command: `vmstat -s` }) + const { stdout: disks } = await executeSSHCmd({ dockerId: id, command: `df -m / --output=size,used,pcent|grep -v 'Used'| xargs` }) + const { stdout: cpus } = await executeSSHCmd({ dockerId: id, command: `nproc --all` }) + // const { stdout: cpuUsage } = await executeSSHCmd({ dockerId: id, command: `echo $[100-$(vmstat 1 2|tail -1|awk '{print $15}')]` }) + // console.log(cpuUsage) + const parsed: any = parseFromText(stats) + return { + usage: { + uptime: parsed.bootTime / 1024, + memory: { + totalMemMb: parsed.totalMemoryKB / 1024, + usedMemMb: parsed.usedMemoryKB / 1024, + freeMemMb: parsed.freeMemoryKB / 1024, + usedMemPercentage: (parsed.usedMemoryKB / parsed.totalMemoryKB) * 100, + freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100 + }, + cpu: { + load: 0, + usage: 0, + count: cpus + }, + disk: { + totalGb: (disks.split(' ')[0] / 1024).toFixed(1), + usedGb: (disks.split(' ')[1] / 1024).toFixed(1), + freeGb: (disks.split(' ')[0] - disks.split(' ')[1]).toFixed(1), + usedPercentage: disks.split(' ')[2].replace('%', ''), + freePercentage: 100 - disks.split(' ')[2].replace('%', '') + } + + } + } + } else { + try { + return { + usage: { + uptime: os.uptime(), + memory: await osu.mem.info(), + cpu: { + load: os.loadavg(), + usage: await osu.cpu.usage(), + count: os.cpus().length + }, + disk: await osu.drive.info('/') + } + + }; + } catch ({ status, message }) { + return errorHandler({ status, message }) + } + } + + +} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/servers/index.ts b/apps/api/src/routes/api/v1/servers/index.ts new file mode 100644 index 000000000..7373d8cb4 --- /dev/null +++ b/apps/api/src/routes/api/v1/servers/index.ts @@ -0,0 +1,14 @@ +import { FastifyPluginAsync } from 'fastify'; +import { listServers, showUsage } from './handlers'; + + +const root: FastifyPluginAsync = async (fastify): Promise => { + fastify.addHook('onRequest', async (request) => { + return await request.jwtVerify() + }) + fastify.get('/', async (request) => await listServers(request)); + fastify.get('/usage/:id', async (request) => await showUsage(request)); + +}; + +export default root; diff --git a/apps/api/src/routes/api/v1/servers/types.ts b/apps/api/src/routes/api/v1/servers/types.ts new file mode 100644 index 000000000..26a896b8e --- /dev/null +++ b/apps/api/src/routes/api/v1/servers/types.ts @@ -0,0 +1,27 @@ +import { OnlyId } from "../../../../types" + +export interface SaveTeam extends OnlyId { + Body: { + name: string + } +} +export interface InviteToTeam { + Body: { + email: string, + permission: string, + teamId: string, + teamName: string + } +} +export interface BodyId { + Body: { + id: string + } +} +export interface SetPermission { + Body: { + userId: string, + newPermission: string, + permissionId: string + } +} \ No newline at end of file diff --git a/apps/ui/src/lib/components/Usage.svelte b/apps/ui/src/lib/components/Usage.svelte index a5128c6b6..7d4a82929 100644 --- a/apps/ui/src/lib/components/Usage.svelte +++ b/apps/ui/src/lib/components/Usage.svelte @@ -1,4 +1,5 @@ -
-
-

Hardware Details

-
- {#if $appSession.teamId === '0'} - - {/if} +
+ {#if loading.usage} + + {:else} + + {/if} + {#if server.remoteEngine} +
+ BETA
+ {/if} +
+
+

+ {server.name} +

+
+ {#if server?.remoteIpAddress} +

{server?.remoteIpAddress}

+ {:else} +

localhost

+ {/if} +
+
+ {#if $appSession.teamId === '0'} + + {/if} +
+
+
@@ -82,21 +109,21 @@
Total Memory
- {(usage?.memory.totalMemMb).toFixed(0)}MB + {(usage?.memory?.totalMemMb).toFixed(0)}MB
Used Memory
- {(usage?.memory.usedMemMb).toFixed(0)}MB + {(usage?.memory?.usedMemMb).toFixed(0)}MB
Free Memory
- {usage?.memory.freeMemPercentage}% + {(usage?.memory?.freeMemPercentage).toFixed(0)}%
@@ -105,41 +132,41 @@
Total CPU
- {usage?.cpu.count} + {usage?.cpu?.count}
CPU Usage
- {usage?.cpu.usage}% + {usage?.cpu?.usage}%
Load Average (5,10,30mins)
-
{usage?.cpu.load}
+
{usage?.cpu?.load}
Total Disk
- {usage?.disk.totalGb}GB + {usage?.disk?.totalGb}GB
Used Disk
- {usage?.disk.usedGb}GB + {usage?.disk?.usedGb}GB
Free Disk
- {usage?.disk.freePercentage}% + {usage?.disk?.freePercentage}%
diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index b9d346da5..e31d228ff 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -132,9 +132,9 @@ White labeled logo
{/if} -
+ Dashboard + Servers
diff --git a/apps/ui/src/routes/servers/index.svelte b/apps/ui/src/routes/servers/index.svelte new file mode 100644 index 000000000..d838e3410 --- /dev/null +++ b/apps/ui/src/routes/servers/index.svelte @@ -0,0 +1,47 @@ + + + + +
+
Servers
+
+
+ {#if servers.length > 0} +
+ {#each servers as server} +
+
+ {#if $appSession.teamId === '0'} + + {/if} +
+
+ {/each} +
+ {:else} +

Nothing here.

+ {/if} +
From 63fa8924aedd35406b876631bcdd5de90205d194 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 14:46:08 +0200 Subject: [PATCH 13/15] fix: autoupdater --- apps/api/src/jobs/infrastructure.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/api/src/jobs/infrastructure.ts b/apps/api/src/jobs/infrastructure.ts index fbcb07616..178a06aca 100644 --- a/apps/api/src/jobs/infrastructure.ts +++ b/apps/api/src/jobs/infrastructure.ts @@ -21,14 +21,17 @@ async function autoUpdater() { const activeCount = 0 if (activeCount === 0) { if (!isDev) { - await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`); - await asyncExecShell(`env | grep COOLIFY > .env`); - await asyncExecShell( - `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=true' .env` - ); - await asyncExecShell( - `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"` - ); + const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); + if (isAutoUpdateEnabled) { + await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`); + await asyncExecShell(`env | grep COOLIFY > .env`); + await asyncExecShell( + `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` + ); + await asyncExecShell( + `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify && docker rm coolify && docker compose up -d --force-recreate"` + ); + } } else { console.log('Updating (not really in dev mode).'); } From 18e899d15ef4ed49929d8056c6b8823985444ba9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 15:00:54 +0200 Subject: [PATCH 14/15] disable remote servers for now --- apps/api/src/routes/api/v1/servers/handlers.ts | 6 ++++-- apps/ui/src/routes/servers/index.svelte | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/api/src/routes/api/v1/servers/handlers.ts b/apps/api/src/routes/api/v1/servers/handlers.ts index 9174d9bf0..c917836d2 100644 --- a/apps/api/src/routes/api/v1/servers/handlers.ts +++ b/apps/api/src/routes/api/v1/servers/handlers.ts @@ -8,9 +8,11 @@ export async function listServers(request: FastifyRequest) { try { const userId = request.user.userId; const teamId = request.user.teamId; - const remoteServers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] }) + const servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } }, remoteEngine: false }, distinct: ['engine'] }) + // const remoteServers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] }) + return { - servers: remoteServers + servers } } catch ({ status, message }) { return errorHandler({ status, message }) diff --git a/apps/ui/src/routes/servers/index.svelte b/apps/ui/src/routes/servers/index.svelte index d838e3410..69d61302e 100644 --- a/apps/ui/src/routes/servers/index.svelte +++ b/apps/ui/src/routes/servers/index.svelte @@ -45,3 +45,4 @@

Nothing here.

{/if}
+
Remote servers will be here soon
From aa6c56b63d54cfd065306af7428747591555f284 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 8 Sep 2022 15:23:01 +0200 Subject: [PATCH 15/15] do not show servers to non admins --- apps/ui/src/routes/__layout.svelte | 52 +++++++++++++------------ apps/ui/src/routes/servers/index.svelte | 6 ++- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index e31d228ff..8694dfe45 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -159,32 +159,34 @@ - - - - - - - - - + + + + + + + + + {/if}
Dashboard Servers diff --git a/apps/ui/src/routes/servers/index.svelte b/apps/ui/src/routes/servers/index.svelte index 69d61302e..cb9ef3d17 100644 --- a/apps/ui/src/routes/servers/index.svelte +++ b/apps/ui/src/routes/servers/index.svelte @@ -23,6 +23,10 @@
@@ -42,7 +46,7 @@ {/each}
{:else} -

Nothing here.

+

Nothing here.

{/if}
Remote servers will be here soon