commit
						ac5cc8b299
					
				| @ -472,7 +472,6 @@ export const saveBuildLog = async ({ | |||||||
| 
 | 
 | ||||||
| 	if (isDev) { | 	if (isDev) { | ||||||
| 		console.debug(`[${applicationId}] ${addTimestamp}`); | 		console.debug(`[${applicationId}] ${addTimestamp}`); | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 	try { | 	try { | ||||||
| 		return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, { | 		return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, { | ||||||
| @ -481,6 +480,7 @@ export const saveBuildLog = async ({ | |||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
|  | 		if (isDev) return | ||||||
| 		return await prisma.buildLog.create({ | 		return await prisma.buildLog.create({ | ||||||
| 			data: { | 			data: { | ||||||
| 				line: addTimestamp, buildId, time: Number(day().valueOf()), applicationId | 				line: addTimestamp, buildId, time: Number(day().valueOf()), applicationId | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ import { scheduler } from './scheduler'; | |||||||
| import { supportedServiceTypesAndVersions } from './services/supportedVersions'; | import { supportedServiceTypesAndVersions } from './services/supportedVersions'; | ||||||
| import { includeServices } from './services/common'; | 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'; | export const isDev = process.env.NODE_ENV === 'development'; | ||||||
| 
 | 
 | ||||||
| const algorithm = 'aes-256-ctr'; | const algorithm = 'aes-256-ctr'; | ||||||
|  | |||||||
| @ -1883,11 +1883,11 @@ async function stopServiceContainers(request: FastifyRequest<ServiceStartStop>) | |||||||
|         if (destinationDockerId) { |         if (destinationDockerId) { | ||||||
|             await executeDockerCmd({ |             await executeDockerCmd({ | ||||||
|                 dockerId: destinationDockerId, |                 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({ |             await executeDockerCmd({ | ||||||
|                 dockerId: destinationDockerId, |                 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 {} |             return {} | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -69,6 +69,43 @@ export async function getImages(request: FastifyRequest<GetImages>) { | |||||||
|         return errorHandler({ status, message }) |         return errorHandler({ status, message }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | export async function cleanupUnconfiguredApplications(request: FastifyRequest<any>) { | ||||||
|  |     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<OnlyId>) { | export async function getApplicationStatus(request: FastifyRequest<OnlyId>) { | ||||||
|     try { |     try { | ||||||
|         const { id } = request.params |         const { id } = request.params | ||||||
| @ -761,7 +798,10 @@ export async function saveBuildPack(request, reply) { | |||||||
|     try { |     try { | ||||||
|         const { id } = request.params |         const { id } = request.params | ||||||
|         const { buildPack } = request.body |         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() |         return reply.code(201).send() | ||||||
|     } catch ({ status, message }) { |     } catch ({ status, message }) { | ||||||
|         return errorHandler({ status, message }) |         return errorHandler({ status, message }) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { FastifyPluginAsync } from 'fastify'; | import { FastifyPluginAsync } from 'fastify'; | ||||||
| import { OnlyId } from '../../../../types'; | 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'; | 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<void> => { | |||||||
|     fastify.get('/', async (request) => await listApplications(request)); |     fastify.get('/', async (request) => await listApplications(request)); | ||||||
|     fastify.post<GetImages>('/images', async (request) => await getImages(request)); |     fastify.post<GetImages>('/images', async (request) => await getImages(request)); | ||||||
| 
 | 
 | ||||||
|  |     fastify.post<any>('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredApplications(request)); | ||||||
|  | 
 | ||||||
|     fastify.post('/new', async (request, reply) => await newApplication(request, reply)); |     fastify.post('/new', async (request, reply) => await newApplication(request, reply)); | ||||||
| 
 | 
 | ||||||
|     fastify.get<OnlyId>('/:id', async (request) => await getApplication(request)); |     fastify.get<OnlyId>('/:id', async (request) => await getApplication(request)); | ||||||
|  | |||||||
| @ -51,6 +51,30 @@ export async function newDatabase(request: FastifyRequest, reply: FastifyReply) | |||||||
|         return errorHandler({ status, message }) |         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<OnlyId>) { | export async function getDatabaseStatus(request: FastifyRequest<OnlyId>) { | ||||||
|     try { |     try { | ||||||
|         const { id } = request.params; |         const { id } = request.params; | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { FastifyPluginAsync } from 'fastify'; | 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'; | import type { OnlyId } from '../../../../types'; | ||||||
| 
 | 
 | ||||||
| @ -12,6 +12,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => { | |||||||
|     fastify.get('/', async (request) => await listDatabases(request)); |     fastify.get('/', async (request) => await listDatabases(request)); | ||||||
|     fastify.post('/new', async (request, reply) => await newDatabase(request, reply)); |     fastify.post('/new', async (request, reply) => await newDatabase(request, reply)); | ||||||
| 
 | 
 | ||||||
|  |     fastify.post<any>('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredDatabases(request)); | ||||||
|  | 
 | ||||||
|     fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request)); |     fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request)); | ||||||
|     fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply)); |     fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply)); | ||||||
|     fastify.delete<DeleteDatabase>('/:id', async (request) => await deleteDatabase(request)); |     fastify.delete<DeleteDatabase>('/:id', async (request) => await deleteDatabase(request)); | ||||||
|  | |||||||
| @ -122,7 +122,7 @@ export async function showDashboard(request: FastifyRequest) { | |||||||
| 	try { | 	try { | ||||||
| 		const userId = request.user.userId; | 		const userId = request.user.userId; | ||||||
| 		const teamId = request.user.teamId; | 		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 } } }, | 			where: { teams: { some: { id: teamId === "0" ? undefined : teamId } } }, | ||||||
| 			include: { settings: true, destinationDocker: true, teams: true }, | 			include: { settings: true, destinationDocker: true, teams: true }, | ||||||
| 		}); | 		}); | ||||||
| @ -143,7 +143,29 @@ export async function showDashboard(request: FastifyRequest) { | |||||||
| 			include: { teams: true }, | 			include: { teams: true }, | ||||||
| 		}); | 		}); | ||||||
| 		const settings = await listSettings(); | 		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 { | 		return { | ||||||
|  | 			foundUnconfiguredApplication, | ||||||
|  | 			foundUnconfiguredDatabase, | ||||||
|  | 			foundUnconfiguredService, | ||||||
| 			applications, | 			applications, | ||||||
| 			databases, | 			databases, | ||||||
| 			services, | 			services, | ||||||
|  | |||||||
| @ -36,6 +36,33 @@ export async function newService(request: FastifyRequest, reply: FastifyReply) { | |||||||
|         return errorHandler({ status, message }) |         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<OnlyId>) { | export async function getServiceStatus(request: FastifyRequest<OnlyId>) { | ||||||
|     try { |     try { | ||||||
|         const teamId = request.user.teamId; |         const teamId = request.user.teamId; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import { | |||||||
|     checkService, |     checkService, | ||||||
|     checkServiceDomain, |     checkServiceDomain, | ||||||
|     cleanupPlausibleLogs, |     cleanupPlausibleLogs, | ||||||
|  |     cleanupUnconfiguredServices, | ||||||
|     deleteService, |     deleteService, | ||||||
|     deleteServiceSecret, |     deleteServiceSecret, | ||||||
|     deleteServiceStorage, |     deleteServiceStorage, | ||||||
| @ -39,6 +40,8 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => { | |||||||
|     fastify.get('/', async (request) => await listServices(request)); |     fastify.get('/', async (request) => await listServices(request)); | ||||||
|     fastify.post('/new', async (request, reply) => await newService(request, reply)); |     fastify.post('/new', async (request, reply) => await newService(request, reply)); | ||||||
| 
 | 
 | ||||||
|  |     fastify.post<any>('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredServices(request)); | ||||||
|  | 
 | ||||||
|     fastify.get<OnlyId>('/:id', async (request) => await getService(request)); |     fastify.get<OnlyId>('/:id', async (request) => await getService(request)); | ||||||
|     fastify.post<SaveService>('/:id', async (request, reply) => await saveService(request, reply)); |     fastify.post<SaveService>('/:id', async (request, reply) => await saveService(request, reply)); | ||||||
|     fastify.delete<OnlyId>('/:id', async (request) => await deleteService(request)); |     fastify.delete<OnlyId>('/:id', async (request) => await deleteService(request)); | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ | |||||||
| <div class="dropdown dropdown-bottom"> | <div class="dropdown dropdown-bottom"> | ||||||
| 	<slot> | 	<slot> | ||||||
| 		<label for="new" tabindex="0" class="btn btn-sm text-sm bg-coollabs hover:bg-coollabs-100"> | 		<label for="new" tabindex="0" class="btn btn-sm text-sm bg-coollabs hover:bg-coollabs-100"> | ||||||
| 			Create New Resource <svg | 			<svg | ||||||
| 				class="h-6 w-6" | 				class="h-6 w-6" | ||||||
| 				xmlns="http://www.w3.org/2000/svg" | 				xmlns="http://www.w3.org/2000/svg" | ||||||
| 				fill="none" | 				fill="none" | ||||||
| @ -31,8 +31,8 @@ | |||||||
| 					stroke-width="2" | 					stroke-width="2" | ||||||
| 					d="M12 6v6m0 0v6m0-6h6m-6 0H6" | 					d="M12 6v6m0 0v6m0-6h6m-6 0H6" | ||||||
| 				/></svg | 				/></svg | ||||||
| 			></label | 			> Create New Resource | ||||||
| 		> | 		</label> | ||||||
| 	</slot> | 	</slot> | ||||||
| 
 | 
 | ||||||
| 	<ul id="new" tabindex="0" class="dropdown-content menu p-2 shadow bg-coolgray-300 rounded w-52"> | 	<ul id="new" tabindex="0" class="dropdown-content menu p-2 shadow bg-coolgray-300 rounded w-52"> | ||||||
|  | |||||||
| @ -97,6 +97,11 @@ | |||||||
| 
 | 
 | ||||||
| 	async function handleDeploySubmit(forceRebuild = false) { | 	async function handleDeploySubmit(forceRebuild = false) { | ||||||
| 		if (!$isDeploymentEnabled) return; | 		if (!$isDeploymentEnabled) return; | ||||||
|  | 		if (!statusInterval) { | ||||||
|  | 			statusInterval = setInterval(async () => { | ||||||
|  | 				await getStatus(); | ||||||
|  | 			}, 2000); | ||||||
|  | 		} | ||||||
| 		try { | 		try { | ||||||
| 			const { buildId } = await post(`/applications/${id}/deploy`, { | 			const { buildId } = await post(`/applications/${id}/deploy`, { | ||||||
| 				...application, | 				...application, | ||||||
| @ -235,7 +240,6 @@ | |||||||
| 					</button> | 					</button> | ||||||
| 				{/if} | 				{/if} | ||||||
| 			</div> | 			</div> | ||||||
| 
 |  | ||||||
| 		{/if} | 		{/if} | ||||||
| 	</nav> | 	</nav> | ||||||
| 	<div | 	<div | ||||||
|  | |||||||
| @ -153,7 +153,7 @@ | |||||||
| {:else} | {:else} | ||||||
| 	<form on:submit|preventDefault={handleSubmit} class="px-10"> | 	<form on:submit|preventDefault={handleSubmit} class="px-10"> | ||||||
| 		<div class="flex lg:flex-row flex-col lg:space-y-0 space-y-2 space-x-0 lg:space-x-2 items-center lg:justify-center"> | 		<div class="flex lg:flex-row flex-col lg:space-y-0 space-y-2 space-x-0 lg:space-x-2 items-center lg:justify-center"> | ||||||
| 				<div class="custom-select-wrapper"><label for="repository" class="pb-1">Repository</label> | 				<div class="custom-select-wrapper w-full"><label for="repository" class="pb-1">Repository</label> | ||||||
| 					<Select | 					<Select | ||||||
| 						placeholder={loading.repositories | 						placeholder={loading.repositories | ||||||
| 							? $t('application.configuration.loading_repositories') | 							? $t('application.configuration.loading_repositories') | ||||||
| @ -168,7 +168,7 @@ | |||||||
| 					/> | 					/> | ||||||
| 				</div> | 				</div> | ||||||
| 				<input class="hidden" bind:value={selected.projectId} name="projectId" /> | 				<input class="hidden" bind:value={selected.projectId} name="projectId" /> | ||||||
| 				<div class="custom-select-wrapper"><label for="repository" class="pb-1">Branch</label> | 				<div class="custom-select-wrapper w-full"><label for="repository" class="pb-1">Branch</label> | ||||||
| 					<Select | 					<Select | ||||||
| 						placeholder={loading.branches | 						placeholder={loading.branches | ||||||
| 							? $t('application.configuration.loading_branches') | 							? $t('application.configuration.loading_branches') | ||||||
|  | |||||||
| @ -328,8 +328,10 @@ | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <form on:submit={handleSubmit}> | <form on:submit={handleSubmit}> | ||||||
| 	<div class="flex lg:flex-row flex-col lg:space-y-0 space-y-2 space-x-0 lg:space-x-2 items-center lg:justify-center"> | 	<div | ||||||
| 		<div class="custom-select-wrapper"> | 		class="flex lg:flex-row flex-col lg:space-y-0 space-y-2 space-x-0 lg:space-x-2 items-center lg:justify-center lg:px-0 px-8" | ||||||
|  | 	> | ||||||
|  | 		<div class="custom-select-wrapper w-full"> | ||||||
| 			<label for="groups" class="pb-1">Groups</label> | 			<label for="groups" class="pb-1">Groups</label> | ||||||
| 			<Select | 			<Select | ||||||
| 				placeholder={loading.base | 				placeholder={loading.base | ||||||
| @ -355,7 +357,7 @@ | |||||||
| 				optionIdentifier="id" | 				optionIdentifier="id" | ||||||
| 			/> | 			/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="custom-select-wrapper"> | 		<div class="custom-select-wrapper w-full"> | ||||||
| 			<label for="projects" class="pb-1">Projects</label> | 			<label for="projects" class="pb-1">Projects</label> | ||||||
| 			<Select | 			<Select | ||||||
| 				placeholder={loading.projects | 				placeholder={loading.projects | ||||||
| @ -381,7 +383,7 @@ | |||||||
| 				isSearchable={true} | 				isSearchable={true} | ||||||
| 			/> | 			/> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="custom-select-wrapper"> | 		<div class="custom-select-wrapper w-full"> | ||||||
| 			<label for="branches" class="pb-1">Branches</label> | 			<label for="branches" class="pb-1">Branches</label> | ||||||
| 			<Select | 			<Select | ||||||
| 				placeholder={loading.branches | 				placeholder={loading.branches | ||||||
|  | |||||||
| @ -172,7 +172,6 @@ | |||||||
| 			 | 			 | ||||||
| 			<div class="custom-select-wrapper"> | 			<div class="custom-select-wrapper"> | ||||||
| 				<Select | 				<Select | ||||||
| 					class="w-full" |  | ||||||
| 					placeholder={loading.branches | 					placeholder={loading.branches | ||||||
| 						? $t('application.configuration.loading_branches') | 						? $t('application.configuration.loading_branches') | ||||||
| 						: branchSelectOptions.length ===0 | 						: branchSelectOptions.length ===0 | ||||||
|  | |||||||
| @ -26,8 +26,6 @@ | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { t } from '$lib/translations'; |  | ||||||
| 
 |  | ||||||
| 	export let application: any; | 	export let application: any; | ||||||
| 	export let appId: string; | 	export let appId: string; | ||||||
| 	export let settings: any; | 	export let settings: any; | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { page, session } from '$app/stores'; | 	import { page } from '$app/stores'; | ||||||
| 	import { goto } from '$app/navigation'; | 	import { goto } from '$app/navigation'; | ||||||
| 	import { get, post } from '$lib/api'; | 	import { get, post } from '$lib/api'; | ||||||
| 	import { t } from '$lib/translations'; | 	import { t } from '$lib/translations'; | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ | |||||||
| 	let fromDb = false; | 	let fromDb = false; | ||||||
| 	let cancelInprogress = false; | 	let cancelInprogress = false; | ||||||
| 	let position = 0; | 	let position = 0; | ||||||
|  | 	let loading = true; | ||||||
| 	const { id } = $page.params; | 	const { id } = $page.params; | ||||||
| 
 | 
 | ||||||
| 	const cleanAnsiCodes = (str: string) => str.replace(/\x1B\[(\d+)m/g, ''); | 	const cleanAnsiCodes = (str: string) => str.replace(/\x1B\[(\d+)m/g, ''); | ||||||
| @ -46,6 +47,7 @@ | |||||||
| 	} | 	} | ||||||
| 	async function streamLogs(sequence = 0) { | 	async function streamLogs(sequence = 0) { | ||||||
| 		try { | 		try { | ||||||
|  | 			loading = true; | ||||||
| 			let { | 			let { | ||||||
| 				logs: responseLogs, | 				logs: responseLogs, | ||||||
| 				status, | 				status, | ||||||
| @ -60,6 +62,7 @@ | |||||||
| 
 | 
 | ||||||
| 			streamInterval = setInterval(async () => { | 			streamInterval = setInterval(async () => { | ||||||
| 				if (status !== 'running' && status !== 'queued') { | 				if (status !== 'running' && status !== 'queued') { | ||||||
|  | 					loading = false; | ||||||
| 					clearInterval(streamInterval); | 					clearInterval(streamInterval); | ||||||
| 					return; | 					return; | ||||||
| 				} | 				} | ||||||
| @ -75,6 +78,7 @@ | |||||||
| 					logs = logs.concat( | 					logs = logs.concat( | ||||||
| 						data.logs.map((log: any) => ({ ...log, line: cleanAnsiCodes(log.line) })) | 						data.logs.map((log: any) => ({ ...log, line: cleanAnsiCodes(log.line) })) | ||||||
| 					); | 					); | ||||||
|  | 					loading = false; | ||||||
| 				} catch (error) { | 				} catch (error) { | ||||||
| 					return errorNotification(error); | 					return errorNotification(error); | ||||||
| 				} | 				} | ||||||
| @ -171,13 +175,13 @@ | |||||||
| 	<div | 	<div | ||||||
| 		bind:this={logsEl} | 		bind:this={logsEl} | ||||||
| 		on:scroll={detect} | 		on:scroll={detect} | ||||||
| 		class="font-mono w-full bg-coolgray-100 border border-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1" | 		class="font-mono w-full bg-coolgray-100 border border-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1 whitespace-pre" | ||||||
| 	> | 	> | ||||||
| 		{#each logs as log} | 		{#each logs as log} | ||||||
| 			{#if fromDb} | 			{#if fromDb} | ||||||
| 				<div>{log.line + '\n'}</div> | 				{log.line + '\n'} | ||||||
| 			{:else} | 			{:else} | ||||||
| 				<div>[{day.unix(log.time).format('HH:mm:ss.SSS')}] {log.line + '\n'}</div> | 				[{day.unix(log.time).format('HH:mm:ss.SSS')}] {log.line + '\n'} | ||||||
| 			{/if} | 			{/if} | ||||||
| 		{/each} | 		{/each} | ||||||
| 	</div> | 	</div> | ||||||
| @ -185,6 +189,10 @@ | |||||||
| 	<div | 	<div | ||||||
| 		class="font-mono w-full bg-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col whitespace-nowrap scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1" | 		class="font-mono w-full bg-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col whitespace-nowrap scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1" | ||||||
| 	> | 	> | ||||||
| 		{dev ? 'In development, logs are  shown in the console.' : 'No logs found yet.'} | 		{loading | ||||||
|  | 			? 'Loading logs...' | ||||||
|  | 			: dev | ||||||
|  | 			? 'In development, logs are shown in the console.' | ||||||
|  | 			: 'No logs found yet.'} | ||||||
| 	</div> | 	</div> | ||||||
| {/if} | {/if} | ||||||
|  | |||||||
| @ -55,7 +55,6 @@ | |||||||
| 	export let database: any; | 	export let database: any; | ||||||
| 	import { del, get, post } from '$lib/api'; | 	import { del, get, post } from '$lib/api'; | ||||||
| 	import { t } from '$lib/translations'; | 	import { t } from '$lib/translations'; | ||||||
| 	import { goto } from '$app/navigation'; |  | ||||||
| 	import { page } from '$app/stores'; | 	import { page } from '$app/stores'; | ||||||
| 	import { errorNotification, handlerNotFoundLoad } from '$lib/common'; | 	import { errorNotification, handlerNotFoundLoad } from '$lib/common'; | ||||||
| 	import { appSession, status, isDeploymentEnabled } from '$lib/store'; | 	import { appSession, status, isDeploymentEnabled } from '$lib/store'; | ||||||
|  | |||||||
| @ -21,6 +21,9 @@ | |||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	export let applications: any; | 	export let applications: any; | ||||||
|  | 	export let foundUnconfiguredApplication: boolean; | ||||||
|  | 	export let foundUnconfiguredService: boolean; | ||||||
|  | 	export let foundUnconfiguredDatabase: boolean; | ||||||
| 	export let databases: any; | 	export let databases: any; | ||||||
| 	export let services: any; | 	export let services: any; | ||||||
| 	export let settings: any; | 	export let settings: any; | ||||||
| @ -28,9 +31,9 @@ | |||||||
| 	export let destinations: any; | 	export let destinations: any; | ||||||
| 
 | 
 | ||||||
| 	let filtered: any = setInitials(); | 	let filtered: any = setInitials(); | ||||||
| 	import { get } from '$lib/api'; | 	import { get, post } from '$lib/api'; | ||||||
| 	import { t } from '$lib/translations'; | 	import { t } from '$lib/translations'; | ||||||
| 	import { asyncSleep, getRndInteger } from '$lib/common'; | 	import { asyncSleep, errorNotification, getRndInteger } from '$lib/common'; | ||||||
| 	import { appSession, search } from '$lib/store'; | 	import { appSession, search } from '$lib/store'; | ||||||
| 
 | 
 | ||||||
| 	import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte'; | 	import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte'; | ||||||
| @ -54,31 +57,28 @@ | |||||||
| 	doSearch(); | 	doSearch(); | ||||||
| 
 | 
 | ||||||
| 	async function refreshStatusApplications() { | 	async function refreshStatusApplications() { | ||||||
| 		loading.applications = true; |  | ||||||
| 		noInitialStatus.applications = false; | 		noInitialStatus.applications = false; | ||||||
| 		numberOfGetStatus = 0; | 		numberOfGetStatus = 0; | ||||||
| 		for (const application of applications) { | 		for (const application of applications) { | ||||||
| 			await getStatus(application, true); | 			status[application.id] = 'loading'; | ||||||
|  | 			getStatus(application, true); | ||||||
| 		} | 		} | ||||||
| 		loading.applications = false; |  | ||||||
| 	} | 	} | ||||||
| 	async function refreshStatusServices() { | 	async function refreshStatusServices() { | ||||||
| 		loading.services = true; |  | ||||||
| 		noInitialStatus.services = false; | 		noInitialStatus.services = false; | ||||||
| 		numberOfGetStatus = 0; | 		numberOfGetStatus = 0; | ||||||
| 		for (const service of services) { | 		for (const service of services) { | ||||||
| 			await getStatus(service, true); | 			status[service.id] = 'loading'; | ||||||
|  | 			getStatus(service, true); | ||||||
| 		} | 		} | ||||||
| 		loading.services = false; |  | ||||||
| 	} | 	} | ||||||
| 	async function refreshStatusDatabases() { | 	async function refreshStatusDatabases() { | ||||||
| 		loading.databases = true; |  | ||||||
| 		noInitialStatus.databases = false; | 		noInitialStatus.databases = false; | ||||||
| 		numberOfGetStatus = 0; | 		numberOfGetStatus = 0; | ||||||
| 		for (const database of databases) { | 		for (const database of databases) { | ||||||
| 			await getStatus(database, true); | 			status[database.id] = 'loading'; | ||||||
|  | 			getStatus(database, true); | ||||||
| 		} | 		} | ||||||
| 		loading.databases = false; |  | ||||||
| 	} | 	} | ||||||
| 	function setInitials(onlyOthers: boolean = false) { | 	function setInitials(onlyOthers: boolean = false) { | ||||||
| 		return { | 		return { | ||||||
| @ -325,6 +325,45 @@ | |||||||
| 			filtered = setInitials(); | 			filtered = setInitials(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	async function cleanupApplications() { | ||||||
|  | 		try { | ||||||
|  | 			const sure = confirm( | ||||||
|  | 				'Are you sure? This will delete all UNCONFIGURED applications and their data.' | ||||||
|  | 			); | ||||||
|  | 			if (sure) { | ||||||
|  | 				await post(`/applications/cleanup/unconfigured`, {}); | ||||||
|  | 				return window.location.reload(); | ||||||
|  | 			} | ||||||
|  | 		} catch (error) { | ||||||
|  | 			return errorNotification(error); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	async function cleanupServices() { | ||||||
|  | 		try { | ||||||
|  | 			const sure = confirm( | ||||||
|  | 				'Are you sure? This will delete all UNCONFIGURED services and their data.' | ||||||
|  | 			); | ||||||
|  | 			if (sure) { | ||||||
|  | 				await post(`/services/cleanup/unconfigured`, {}); | ||||||
|  | 				return window.location.reload(); | ||||||
|  | 			} | ||||||
|  | 		} catch (error) { | ||||||
|  | 			return errorNotification(error); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	async function cleanupDatabases() { | ||||||
|  | 		try { | ||||||
|  | 			const sure = confirm( | ||||||
|  | 				'Are you sure? This will delete all UNCONFIGURED databases and their data.' | ||||||
|  | 			); | ||||||
|  | 			if (sure) { | ||||||
|  | 				await post(`/databases/cleanup/unconfigured`, {}); | ||||||
|  | 				return window.location.reload(); | ||||||
|  | 			} | ||||||
|  | 		} catch (error) { | ||||||
|  | 			return errorNotification(error); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <nav class="header"> | <nav class="header"> | ||||||
| @ -521,15 +560,19 @@ | |||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0} | 	{#if (filtered.applications.length > 0 && applications.length > 0) || filtered.otherApplications.length > 0} | ||||||
| 		<div class="flex items-center mt-10"> | 		<div class="flex items-center mt-10 space-x-2"> | ||||||
| 			<h1 class="title lg:text-3xl pr-4">Applications</h1> | 			<h1 class="title lg:text-3xl">Applications</h1> | ||||||
| 			<button | 			<button class="btn btn-sm btn-primary" on:click={refreshStatusApplications} | ||||||
| 				class="btn btn-sm btn-primary" |  | ||||||
| 				class:loading={loading.applications} |  | ||||||
| 				disabled={loading.applications} |  | ||||||
| 				on:click={refreshStatusApplications} |  | ||||||
| 				>{noInitialStatus.applications ? 'Load Status' : 'Refresh Status'}</button | 				>{noInitialStatus.applications ? 'Load Status' : 'Refresh Status'}</button | ||||||
| 			> | 			> | ||||||
|  | 			{#if foundUnconfiguredApplication} | ||||||
|  | 				<button | ||||||
|  | 					class="btn btn-sm" | ||||||
|  | 					class:loading={loading.applications} | ||||||
|  | 					disabled={loading.applications} | ||||||
|  | 					on:click={cleanupApplications}>Cleanup Unconfigured Resources</button | ||||||
|  | 				> | ||||||
|  | 			{/if} | ||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if filtered.applications.length > 0 && applications.length > 0} | 	{#if filtered.applications.length > 0 && applications.length > 0} | ||||||
| @ -547,7 +590,7 @@ | |||||||
| 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 							{:then} | 							{:then} | ||||||
| 								{#if !noInitialStatus.applications} | 								{#if !noInitialStatus.applications} | ||||||
| 									{#if loading.applications} | 									{#if status[application.id] === 'loading'} | ||||||
| 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 									{:else if status[application.id] === 'running'} | 									{:else if status[application.id] === 'running'} | ||||||
| 										<span class="indicator-item badge bg-success badge-sm" /> | 										<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| @ -559,7 +602,7 @@ | |||||||
| 							<div class="w-full flex flex-row"> | 							<div class="w-full flex flex-row"> | ||||||
| 								<ApplicationsIcons {application} isAbsolute={true} /> | 								<ApplicationsIcons {application} isAbsolute={true} /> | ||||||
| 								<div class="w-full flex flex-col"> | 								<div class="w-full flex flex-col"> | ||||||
| 									<h1 class="font-bold text-lg lg:text-xl truncate"> | 									<h1 class="font-bold text-base truncate"> | ||||||
| 										{application.name} | 										{application.name} | ||||||
| 										{#if application.settings?.isBot} | 										{#if application.settings?.isBot} | ||||||
| 											<span class="text-xs badge bg-coolblack border-none text-applications" | 											<span class="text-xs badge bg-coolblack border-none text-applications" | ||||||
| @ -654,7 +697,7 @@ | |||||||
| 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 						{:then} | 						{:then} | ||||||
| 							{#if !noInitialStatus.applications} | 							{#if !noInitialStatus.applications} | ||||||
| 								{#if loading.applications} | 								{#if status[application.id] === 'loading'} | ||||||
| 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 								{:else if status[application.id] === 'running'} | 								{:else if status[application.id] === 'running'} | ||||||
| 									<span class="indicator-item badge bg-success badge-sm" /> | 									<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| @ -666,7 +709,7 @@ | |||||||
| 						<div class="w-full flex flex-row"> | 						<div class="w-full flex flex-row"> | ||||||
| 							<ApplicationsIcons {application} isAbsolute={true} /> | 							<ApplicationsIcons {application} isAbsolute={true} /> | ||||||
| 							<div class="w-full flex flex-col"> | 							<div class="w-full flex flex-col"> | ||||||
| 								<h1 class="font-bold text-lg lg:text-xl truncate"> | 								<h1 class="font-bold text-base truncate"> | ||||||
| 									{application.name} | 									{application.name} | ||||||
| 									{#if application.settings?.isBot} | 									{#if application.settings?.isBot} | ||||||
| 										<span class="text-xs badge bg-coolblack border-none text-applications">BOT</span | 										<span class="text-xs badge bg-coolblack border-none text-applications">BOT</span | ||||||
| @ -740,15 +783,19 @@ | |||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if (filtered.services.length > 0 && services.length > 0) || filtered.otherServices.length > 0} | 	{#if (filtered.services.length > 0 && services.length > 0) || filtered.otherServices.length > 0} | ||||||
| 		<div class="flex items-center mt-10"> | 		<div class="flex items-center mt-10 space-x-2"> | ||||||
| 			<h1 class="title lg:text-3xl pr-4">Services</h1> | 			<h1 class="title lg:text-3xl">Services</h1> | ||||||
| 			<button | 			<button class="btn btn-sm btn-primary" on:click={refreshStatusServices} | ||||||
| 				class="btn btn-sm btn-primary" |  | ||||||
| 				class:loading={loading.services} |  | ||||||
| 				disabled={loading.services} |  | ||||||
| 				on:click={refreshStatusServices} |  | ||||||
| 				>{noInitialStatus.services ? 'Load Status' : 'Refresh Status'}</button | 				>{noInitialStatus.services ? 'Load Status' : 'Refresh Status'}</button | ||||||
| 			> | 			> | ||||||
|  | 			{#if foundUnconfiguredService} | ||||||
|  | 				<button | ||||||
|  | 					class="btn btn-sm" | ||||||
|  | 					class:loading={loading.services} | ||||||
|  | 					disabled={loading.services} | ||||||
|  | 					on:click={cleanupServices}>Cleanup Unconfigured Resources</button | ||||||
|  | 				> | ||||||
|  | 			{/if} | ||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if filtered.services.length > 0 && services.length > 0} | 	{#if filtered.services.length > 0 && services.length > 0} | ||||||
| @ -766,7 +813,7 @@ | |||||||
| 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 							{:then} | 							{:then} | ||||||
| 								{#if !noInitialStatus.services} | 								{#if !noInitialStatus.services} | ||||||
| 									{#if loading.services} | 									{#if status[service.id] === 'loading'} | ||||||
| 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 									{:else if status[service.id] === 'running'} | 									{:else if status[service.id] === 'running'} | ||||||
| 										<span class="indicator-item badge bg-success badge-sm" /> | 										<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| @ -778,7 +825,7 @@ | |||||||
| 							<div class="w-full flex flex-row"> | 							<div class="w-full flex flex-row"> | ||||||
| 								<ServiceIcons type={service.type} isAbsolute={true} /> | 								<ServiceIcons type={service.type} isAbsolute={true} /> | ||||||
| 								<div class="w-full flex flex-col"> | 								<div class="w-full flex flex-col"> | ||||||
| 									<h1 class="font-bold text-lg lg:text-xl truncate">{service.name}</h1> | 									<h1 class="font-bold text-base truncate">{service.name}</h1> | ||||||
| 									<div class="h-10 text-xs"> | 									<div class="h-10 text-xs"> | ||||||
| 										{#if service?.fqdn} | 										{#if service?.fqdn} | ||||||
| 											<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2> | 											<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2> | ||||||
| @ -839,7 +886,7 @@ | |||||||
| 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 						{:then} | 						{:then} | ||||||
| 							{#if !noInitialStatus.services} | 							{#if !noInitialStatus.services} | ||||||
| 								{#if loading.services} | 								{#if status[service.id] === 'loading'} | ||||||
| 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 								{:else if status[service.id] === 'running'} | 								{:else if status[service.id] === 'running'} | ||||||
| 									<span class="indicator-item badge bg-success badge-sm" /> | 									<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| @ -851,7 +898,7 @@ | |||||||
| 						<div class="w-full flex flex-row"> | 						<div class="w-full flex flex-row"> | ||||||
| 							<ServiceIcons type={service.type} isAbsolute={true} /> | 							<ServiceIcons type={service.type} isAbsolute={true} /> | ||||||
| 							<div class="w-full flex flex-col"> | 							<div class="w-full flex flex-col"> | ||||||
| 								<h1 class="font-bold text-lg lg:text-xl truncate">{service.name}</h1> | 								<h1 class="font-bold text-base truncate">{service.name}</h1> | ||||||
| 								<div class="h-10 text-xs"> | 								<div class="h-10 text-xs"> | ||||||
| 									{#if service?.fqdn} | 									{#if service?.fqdn} | ||||||
| 										<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2> | 										<h2>{service?.fqdn.replace('https://', '').replace('http://', '')}</h2> | ||||||
| @ -894,15 +941,19 @@ | |||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if (filtered.databases.length > 0 && databases.length > 0) || filtered.otherDatabases.length > 0} | 	{#if (filtered.databases.length > 0 && databases.length > 0) || filtered.otherDatabases.length > 0} | ||||||
| 		<div class="flex items-center mt-10"> | 		<div class="flex items-center mt-10 space-x-2"> | ||||||
| 			<h1 class="title lg:text-3xl pr-4">Databases</h1> | 			<h1 class="title lg:text-3xl">Databases</h1> | ||||||
| 			<button | 			<button class="btn btn-sm btn-primary" on:click={refreshStatusDatabases} | ||||||
| 				class="btn btn-sm btn-primary" |  | ||||||
| 				on:click={refreshStatusDatabases} |  | ||||||
| 				class:loading={loading.databases} |  | ||||||
| 				disabled={loading.databases} |  | ||||||
| 				>{noInitialStatus.databases ? 'Load Status' : 'Refresh Status'}</button | 				>{noInitialStatus.databases ? 'Load Status' : 'Refresh Status'}</button | ||||||
| 			> | 			> | ||||||
|  | 			{#if foundUnconfiguredDatabase} | ||||||
|  | 				<button | ||||||
|  | 					class="btn btn-sm" | ||||||
|  | 					class:loading={loading.databases} | ||||||
|  | 					disabled={loading.databases} | ||||||
|  | 					on:click={cleanupDatabases}>Cleanup Unconfigured Resources</button | ||||||
|  | 				> | ||||||
|  | 			{/if} | ||||||
| 		</div> | 		</div> | ||||||
| 	{/if} | 	{/if} | ||||||
| 	{#if filtered.databases.length > 0 && databases.length > 0} | 	{#if filtered.databases.length > 0 && databases.length > 0} | ||||||
| @ -920,9 +971,9 @@ | |||||||
| 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 								<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 							{:then} | 							{:then} | ||||||
| 								{#if !noInitialStatus.databases} | 								{#if !noInitialStatus.databases} | ||||||
| 									{#if loading.databases} | 									{#if status[database.id] === 'loading'} | ||||||
| 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 										<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 									{:else if status[databases.id] === 'running'} | 									{:else if status[database.id] === 'running'} | ||||||
| 										<span class="indicator-item badge bg-success badge-sm" /> | 										<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| 									{:else} | 									{:else} | ||||||
| 										<span class="indicator-item badge bg-error badge-sm" /> | 										<span class="indicator-item badge bg-error badge-sm" /> | ||||||
| @ -933,7 +984,7 @@ | |||||||
| 								<DatabaseIcons type={database.type} isAbsolute={true} /> | 								<DatabaseIcons type={database.type} isAbsolute={true} /> | ||||||
| 								<div class="w-full flex flex-col"> | 								<div class="w-full flex flex-col"> | ||||||
| 									<div class="h-10"> | 									<div class="h-10"> | ||||||
| 										<h1 class="font-bold text-lg lg:text-xl truncate">{database.name}</h1> | 										<h1 class="font-bold text-base truncate">{database.name}</h1> | ||||||
| 										<div class="h-10 text-xs"> | 										<div class="h-10 text-xs"> | ||||||
| 											{#if database?.version} | 											{#if database?.version} | ||||||
| 												<h2 class="">{database?.version}</h2> | 												<h2 class="">{database?.version}</h2> | ||||||
| @ -997,9 +1048,9 @@ | |||||||
| 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 							<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 						{:then} | 						{:then} | ||||||
| 							{#if !noInitialStatus.databases} | 							{#if !noInitialStatus.databases} | ||||||
| 								{#if loading.databases} | 								{#if status[database.id] === 'loading'} | ||||||
| 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | 									<span class="indicator-item badge bg-yellow-300 badge-sm" /> | ||||||
| 								{:else if status[databases.id] === 'running'} | 								{:else if status[database.id] === 'running'} | ||||||
| 									<span class="indicator-item badge bg-success badge-sm" /> | 									<span class="indicator-item badge bg-success badge-sm" /> | ||||||
| 								{:else} | 								{:else} | ||||||
| 									<span class="indicator-item badge bg-error badge-sm" /> | 									<span class="indicator-item badge bg-error badge-sm" /> | ||||||
| @ -1010,7 +1061,7 @@ | |||||||
| 							<DatabaseIcons type={database.type} isAbsolute={true} /> | 							<DatabaseIcons type={database.type} isAbsolute={true} /> | ||||||
| 							<div class="w-full flex flex-col"> | 							<div class="w-full flex flex-col"> | ||||||
| 								<div class="h-10"> | 								<div class="h-10"> | ||||||
| 									<h1 class="font-bold text-lg lg:text-xl truncate">{database.name}</h1> | 									<h1 class="font-bold text-base truncate">{database.name}</h1> | ||||||
| 									<div class="h-10 text-xs"> | 									<div class="h-10 text-xs"> | ||||||
| 										{#if database?.version} | 										{#if database?.version} | ||||||
| 											<h2 class="">{database?.version}</h2> | 											<h2 class="">{database?.version}</h2> | ||||||
| @ -1129,7 +1180,7 @@ | |||||||
| 								</div> | 								</div> | ||||||
| 								<div class="w-full flex flex-col"> | 								<div class="w-full flex flex-col"> | ||||||
| 									<div class="h-10"> | 									<div class="h-10"> | ||||||
| 										<h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1> | 										<h1 class="font-bold text-base truncate">{source.name}</h1> | ||||||
| 										{#if source.teams.length > 0 && source.teams[0]?.name} | 										{#if source.teams.length > 0 && source.teams[0]?.name} | ||||||
| 											<div class="truncate text-xs">{source.teams[0]?.name}</div> | 											<div class="truncate text-xs">{source.teams[0]?.name}</div> | ||||||
| 										{/if} | 										{/if} | ||||||
| @ -1218,7 +1269,7 @@ | |||||||
| 							</div> | 							</div> | ||||||
| 							<div class="w-full flex flex-col"> | 							<div class="w-full flex flex-col"> | ||||||
| 								<div class="h-10"> | 								<div class="h-10"> | ||||||
| 									<h1 class="font-bold text-lg lg:text-xl truncate">{source.name}</h1> | 									<h1 class="font-bold text-base truncate">{source.name}</h1> | ||||||
| 									{#if source.teams.length > 0 && source.teams[0]?.name} | 									{#if source.teams.length > 0 && source.teams[0]?.name} | ||||||
| 										<div class="truncate text-xs">{source.teams[0]?.name}</div> | 										<div class="truncate text-xs">{source.teams[0]?.name}</div> | ||||||
| 									{/if} | 									{/if} | ||||||
| @ -1292,7 +1343,7 @@ | |||||||
| 									{/if} | 									{/if} | ||||||
| 								</div> | 								</div> | ||||||
| 								<div class="w-full flex flex-col"> | 								<div class="w-full flex flex-col"> | ||||||
| 									<h1 class="font-bold text-lg lg:text-xl truncate">{destination.name}</h1> | 									<h1 class="font-bold text-base truncate">{destination.name}</h1> | ||||||
| 									<div class="h-10 text-xs"> | 									<div class="h-10 text-xs"> | ||||||
| 										{#if $appSession.teamId === '0' && destination.remoteVerified === false && destination.remoteEngine} | 										{#if $appSession.teamId === '0' && destination.remoteVerified === false && destination.remoteEngine} | ||||||
| 											<h2 class="text-red-500">Not verified yet</h2> | 											<h2 class="text-red-500">Not verified yet</h2> | ||||||
| @ -1373,7 +1424,7 @@ | |||||||
| 								{/if} | 								{/if} | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="w-full flex flex-col"> | 							<div class="w-full flex flex-col"> | ||||||
| 								<h1 class="font-bold text-lg lg:text-xl truncate">{destination.name}</h1> | 								<h1 class="font-bold text-base truncate">{destination.name}</h1> | ||||||
| 								<div class="h-10 text-xs"> | 								<div class="h-10 text-xs"> | ||||||
| 									{#if $appSession.teamId === '0' && destination.remoteVerified === false && destination.remoteEngine} | 									{#if $appSession.teamId === '0' && destination.remoteVerified === false && destination.remoteEngine} | ||||||
| 										<h2 class="text-red-500">Not verified yet</h2> | 										<h2 class="text-red-500">Not verified yet</h2> | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ textarea { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #svelte .custom-select-wrapper .selectContainer { | #svelte .custom-select-wrapper .selectContainer { | ||||||
| 	@apply h-12 w-96 rounded bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm ; | 	@apply h-12 rounded bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm ; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #svelte .listContainer { | #svelte .listContainer { | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "name": "coolify", |   "name": "coolify", | ||||||
|   "description": "An open-source & self-hostable Heroku / Netlify alternative.", |   "description": "An open-source & self-hostable Heroku / Netlify alternative.", | ||||||
|   "version": "3.10.11", |   "version": "3.10.12", | ||||||
|   "license": "Apache-2.0", |   "license": "Apache-2.0", | ||||||
|   "repository": "github:coollabsio/coolify", |   "repository": "github:coollabsio/coolify", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user