diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts index c5d589bda..98a387f2f 100644 --- a/apps/api/src/lib/buildPacks/common.ts +++ b/apps/api/src/lib/buildPacks/common.ts @@ -472,15 +472,15 @@ export const saveBuildLog = async ({ if (isDev) { console.debug(`[${applicationId}] ${addTimestamp}`); - return - } + } try { return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, { json: { line: encrypt(line) } }) - } catch(error) { + } catch (error) { + if (isDev) return return await prisma.buildLog.create({ data: { line: addTimestamp, buildId, time: Number(day().valueOf()), applicationId diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index d9cb80a58..e8d12c97e 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.10.11'; +export const version = '3.10.12'; export const isDev = process.env.NODE_ENV === 'development'; const algorithm = 'aes-256-ctr'; diff --git a/apps/api/src/lib/services/handlers.ts b/apps/api/src/lib/services/handlers.ts index e951004fa..ee9ab88ec 100644 --- a/apps/api/src/lib/services/handlers.ts +++ b/apps/api/src/lib/services/handlers.ts @@ -1883,11 +1883,11 @@ async function stopServiceContainers(request: FastifyRequest) if (destinationDockerId) { await executeDockerCmd({ dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}|xargs -n 1 docker stop -t 0` + command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}|xargs -r -n 1 docker stop -t 0` }) await executeDockerCmd({ dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}|xargs -n 1 docker rm --force` + command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}|xargs -r -n 1 docker rm --force` }) return {} } diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts index 70c593728..be06a3519 100644 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ b/apps/api/src/routes/api/v1/applications/handlers.ts @@ -69,6 +69,43 @@ export async function getImages(request: FastifyRequest) { return errorHandler({ status, message }) } } +export async function cleanupUnconfiguredApplications(request: FastifyRequest) { + try { + const teamId = request.user.teamId + let applications = await prisma.application.findMany({ + where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } }, + include: { settings: true, destinationDocker: true, teams: true }, + }); + for (const application of applications) { + if (!application.buildPack || !application.destinationDockerId || !application.branch || (!application.settings?.isBot && !application?.fqdn)) { + if (application?.destinationDockerId && application.destinationDocker?.network) { + const { stdout: containers } = await executeDockerCmd({ + dockerId: application.destinationDocker.id, + command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${application.id} --format '{{json .}}'` + }) + if (containers) { + const containersArray = containers.trim().split('\n'); + for (const container of containersArray) { + const containerObj = JSON.parse(container); + const id = containerObj.ID; + await removeContainer({ id, dockerId: application.destinationDocker.id }); + } + } + } + await prisma.applicationSettings.deleteMany({ where: { applicationId: application.id } }); + await prisma.buildLog.deleteMany({ where: { applicationId: application.id } }); + await prisma.build.deleteMany({ where: { applicationId: application.id } }); + await prisma.secret.deleteMany({ where: { applicationId: application.id } }); + await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: application.id } }); + await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: application.id } }); + await prisma.application.deleteMany({ where: { id: application.id } }); + } + } + return {} + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} export async function getApplicationStatus(request: FastifyRequest) { try { const { id } = request.params @@ -761,7 +798,10 @@ export async function saveBuildPack(request, reply) { try { const { id } = request.params const { buildPack } = request.body - await prisma.application.update({ where: { id }, data: { buildPack } }); + const { baseImage, baseBuildImage } = setDefaultBaseImage( + buildPack + ); + await prisma.application.update({ where: { id }, data: { buildPack, baseImage, baseBuildImage } }); return reply.code(201).send() } catch ({ status, message }) { return errorHandler({ status, message }) diff --git a/apps/api/src/routes/api/v1/applications/index.ts b/apps/api/src/routes/api/v1/applications/index.ts index af39e63ce..26e7ff16a 100644 --- a/apps/api/src/routes/api/v1/applications/index.ts +++ b/apps/api/src/routes/api/v1/applications/index.ts @@ -1,6 +1,6 @@ import { FastifyPluginAsync } from 'fastify'; import { OnlyId } from '../../../../types'; -import { cancelDeployment, checkDNS, checkDomain, checkRepository, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers'; +import { cancelDeployment, checkDNS, checkDomain, checkRepository, cleanupUnconfiguredApplications, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers'; import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuilds, GetImages, RestartPreviewApplication, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types'; @@ -11,6 +11,8 @@ const root: FastifyPluginAsync = async (fastify): Promise => { fastify.get('/', async (request) => await listApplications(request)); fastify.post('/images', async (request) => await getImages(request)); + fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredApplications(request)); + fastify.post('/new', async (request, reply) => await newApplication(request, reply)); fastify.get('/:id', async (request) => await getApplication(request)); diff --git a/apps/api/src/routes/api/v1/databases/handlers.ts b/apps/api/src/routes/api/v1/databases/handlers.ts index 16d43205c..48a29a900 100644 --- a/apps/api/src/routes/api/v1/databases/handlers.ts +++ b/apps/api/src/routes/api/v1/databases/handlers.ts @@ -51,6 +51,30 @@ export async function newDatabase(request: FastifyRequest, reply: FastifyReply) return errorHandler({ status, message }) } } +export async function cleanupUnconfiguredDatabases(request: FastifyRequest) { + try { + const teamId = request.user.teamId; + let databases = await prisma.database.findMany({ + where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } }, + include: { settings: true, destinationDocker: true, teams: true }, + }); + for (const database of databases) { + if (!database?.version) { + const { id } = database; + if (database.destinationDockerId) { + const everStarted = await stopDatabaseContainer(database); + if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); + } + await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); + await prisma.databaseSecret.deleteMany({ where: { databaseId: id } }); + await prisma.database.delete({ where: { id } }); + } + } + return {} + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} export async function getDatabaseStatus(request: FastifyRequest) { try { const { id } = request.params; diff --git a/apps/api/src/routes/api/v1/databases/index.ts b/apps/api/src/routes/api/v1/databases/index.ts index 8f269c8b5..65f0d58f4 100644 --- a/apps/api/src/routes/api/v1/databases/index.ts +++ b/apps/api/src/routes/api/v1/databases/index.ts @@ -1,5 +1,5 @@ import { FastifyPluginAsync } from 'fastify'; -import { deleteDatabase, deleteDatabaseSecret, getDatabase, getDatabaseLogs, getDatabaseSecrets, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSecret, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers'; +import { cleanupUnconfiguredDatabases, deleteDatabase, deleteDatabaseSecret, getDatabase, getDatabaseLogs, getDatabaseSecrets, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSecret, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers'; import type { OnlyId } from '../../../../types'; @@ -12,6 +12,8 @@ const root: FastifyPluginAsync = async (fastify): Promise => { fastify.get('/', async (request) => await listDatabases(request)); fastify.post('/new', async (request, reply) => await newDatabase(request, reply)); + fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredDatabases(request)); + fastify.get('/:id', async (request) => await getDatabase(request)); fastify.post('/:id', async (request, reply) => await saveDatabase(request, reply)); fastify.delete('/:id', async (request) => await deleteDatabase(request)); diff --git a/apps/api/src/routes/api/v1/handlers.ts b/apps/api/src/routes/api/v1/handlers.ts index 117bb9d13..c57766ed6 100644 --- a/apps/api/src/routes/api/v1/handlers.ts +++ b/apps/api/src/routes/api/v1/handlers.ts @@ -122,7 +122,7 @@ export async function showDashboard(request: FastifyRequest) { try { const userId = request.user.userId; const teamId = request.user.teamId; - const applications = await prisma.application.findMany({ + let applications = await prisma.application.findMany({ where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } }, include: { settings: true, destinationDocker: true, teams: true }, }); @@ -143,7 +143,29 @@ export async function showDashboard(request: FastifyRequest) { include: { teams: true }, }); const settings = await listSettings(); + + let foundUnconfiguredApplication = false; + for (const application of applications) { + if (!application.buildPack || !application.destinationDockerId || !application.branch || (!application.settings?.isBot && !application?.fqdn)) { + foundUnconfiguredApplication = true + } + } + let foundUnconfiguredService = false; + for (const service of services) { + if (!service.fqdn) { + foundUnconfiguredService = true + } + } + let foundUnconfiguredDatabase = false; + for (const database of databases) { + if (!database.version) { + foundUnconfiguredDatabase = true + } + } return { + foundUnconfiguredApplication, + foundUnconfiguredDatabase, + foundUnconfiguredService, applications, databases, services, diff --git a/apps/api/src/routes/api/v1/services/handlers.ts b/apps/api/src/routes/api/v1/services/handlers.ts index 2207841d2..2dcc434b6 100644 --- a/apps/api/src/routes/api/v1/services/handlers.ts +++ b/apps/api/src/routes/api/v1/services/handlers.ts @@ -36,6 +36,33 @@ export async function newService(request: FastifyRequest, reply: FastifyReply) { return errorHandler({ status, message }) } } +export async function cleanupUnconfiguredServices(request: FastifyRequest) { + try { + const teamId = request.user.teamId; + let services = await prisma.service.findMany({ + where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } }, + include: { destinationDocker: true, teams: true }, + }); + for (const service of services) { + if (!service.fqdn) { + if (service.destinationDockerId) { + await executeDockerCmd({ + dockerId: service.destinationDockerId, + command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}|xargs -r -n 1 docker stop -t 0` + }) + await executeDockerCmd({ + dockerId: service.destinationDockerId, + command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}|xargs -r -n 1 docker rm --force` + }) + } + await removeService({ id: service.id }); + } + } + return {} + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} export async function getServiceStatus(request: FastifyRequest) { try { const teamId = request.user.teamId; diff --git a/apps/api/src/routes/api/v1/services/index.ts b/apps/api/src/routes/api/v1/services/index.ts index 17ac60fdd..bd0311b4d 100644 --- a/apps/api/src/routes/api/v1/services/index.ts +++ b/apps/api/src/routes/api/v1/services/index.ts @@ -5,6 +5,7 @@ import { checkService, checkServiceDomain, cleanupPlausibleLogs, + cleanupUnconfiguredServices, deleteService, deleteServiceSecret, deleteServiceStorage, @@ -39,6 +40,8 @@ const root: FastifyPluginAsync = async (fastify): Promise => { fastify.get('/', async (request) => await listServices(request)); fastify.post('/new', async (request, reply) => await newService(request, reply)); + fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredServices(request)); + fastify.get('/:id', async (request) => await getService(request)); fastify.post('/:id', async (request, reply) => await saveService(request, reply)); fastify.delete('/:id', async (request) => await deleteService(request)); diff --git a/apps/ui/src/routes/_NewResource.svelte b/apps/ui/src/routes/_NewResource.svelte index e3c494aa3..84e115c5c 100644 --- a/apps/ui/src/routes/_NewResource.svelte +++ b/apps/ui/src/routes/_NewResource.svelte @@ -19,7 +19,7 @@ {#if $page.url.pathname.startsWith(`/applications/${id}/configuration/`)} -
- {#if forceDelete} - - {:else} - - {/if} -
- +
+ {#if forceDelete} + + {:else} + + {/if} +
{/if}
-
+
-
+
-
+