feat: add host path to any container
This commit is contained in:
		
							parent
							
								
									3e81d7e9cb
								
							
						
					
					
						commit
						1c237affb4
					
				| @ -0,0 +1,2 @@ | ||||
| -- AlterTable | ||||
| ALTER TABLE "ApplicationPersistentStorage" ADD COLUMN "hostPath" TEXT; | ||||
| @ -195,6 +195,7 @@ model ApplicationSettings { | ||||
| model ApplicationPersistentStorage { | ||||
|   id            String      @id @default(cuid()) | ||||
|   applicationId String | ||||
|   hostPath      String? | ||||
|   path          String | ||||
|   oldPath       Boolean     @default(false) | ||||
|   createdAt     DateTime    @default(now()) | ||||
|  | ||||
| @ -110,6 +110,9 @@ import * as buildpacks from '../lib/buildPacks'; | ||||
| 													.replace(/\//gi, '-') | ||||
| 													.replace('-app', '')}:${storage.path}`;
 | ||||
| 											} | ||||
| 											if (storage.hostPath) { | ||||
| 												return `${storage.hostPath}:${storage.path}` | ||||
| 											} | ||||
| 											return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`; | ||||
| 										}) || []; | ||||
| 
 | ||||
| @ -160,7 +163,11 @@ import * as buildpacks from '../lib/buildPacks'; | ||||
| 											port: exposePort ? `${exposePort}:${port}` : port | ||||
| 										}); | ||||
| 										try { | ||||
| 											const composeVolumes = volumes.map((volume) => { | ||||
| 											const composeVolumes = volumes.filter(v => { | ||||
| 												if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { | ||||
| 													return v; | ||||
| 												} | ||||
| 											}).map((volume) => { | ||||
| 												return { | ||||
| 													[`${volume.split(':')[0]}`]: { | ||||
| 														name: volume.split(':')[0] | ||||
| @ -381,6 +388,9 @@ import * as buildpacks from '../lib/buildPacks'; | ||||
| 												.replace(/\//gi, '-') | ||||
| 												.replace('-app', '')}:${storage.path}`;
 | ||||
| 										} | ||||
| 										if (storage.hostPath) { | ||||
| 											return `${storage.hostPath}:${storage.path}` | ||||
| 										} | ||||
| 										return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`; | ||||
| 									}) || []; | ||||
| 
 | ||||
| @ -691,7 +701,11 @@ import * as buildpacks from '../lib/buildPacks'; | ||||
| 											await saveDockerRegistryCredentials({ url, username, password, workdir }); | ||||
| 										} | ||||
| 										try { | ||||
| 											const composeVolumes = volumes.map((volume) => { | ||||
| 											const composeVolumes = volumes.filter(v => { | ||||
| 												if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { | ||||
| 													return v; | ||||
| 												} | ||||
| 											}).map((volume) => { | ||||
| 												return { | ||||
| 													[`${volume.split(':')[0]}`]: { | ||||
| 														name: volume.split(':')[0] | ||||
|  | ||||
| @ -36,12 +36,13 @@ export default async function (data) { | ||||
| 	if (volumes.length > 0) { | ||||
| 		for (const volume of volumes) { | ||||
| 			let [v, path] = volume.split(':'); | ||||
| 			composeVolumes[v] = { | ||||
| 				name: v | ||||
| 			}; | ||||
| 			if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { | ||||
| 				composeVolumes[v] = { | ||||
| 					name: v | ||||
| 				}; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	let networks = {}; | ||||
| 	for (let [key, value] of Object.entries(dockerComposeYaml.services)) { | ||||
| 		value['container_name'] = `${applicationId}-${key}`; | ||||
| @ -78,6 +79,7 @@ export default async function (data) { | ||||
| 		if (value['volumes']?.length > 0) { | ||||
| 			value['volumes'] = value['volumes'].map((volume) => { | ||||
| 				let [v, path, permission] = volume.split(':'); | ||||
| 				console.log(v, path, permission) | ||||
| 				if ( | ||||
| 					v.startsWith('.') || | ||||
| 					v.startsWith('..') || | ||||
| @ -106,6 +108,7 @@ export default async function (data) { | ||||
| 				value['volumes'].push(volume); | ||||
| 			} | ||||
| 		} | ||||
| 		console.log({ volumes, composeVolumes }) | ||||
| 		if (dockerComposeConfiguration[key]?.port) { | ||||
| 			value['expose'] = [dockerComposeConfiguration[key].port]; | ||||
| 		} | ||||
|  | ||||
| @ -1633,6 +1633,9 @@ export function errorHandler({ | ||||
| 	type?: string | null; | ||||
| }) { | ||||
| 	if (message.message) message = message.message; | ||||
| 	if (message.includes('Unique constraint failed')) { | ||||
| 		message = 'This data is unique and already exists. Please try again with a different value.'; | ||||
| 	} | ||||
| 	if (type === 'normal') { | ||||
| 		Sentry.captureException(message); | ||||
| 	} | ||||
|  | ||||
| @ -1340,16 +1340,16 @@ export async function getStorages(request: FastifyRequest<OnlyId>) { | ||||
| export async function saveStorage(request: FastifyRequest<SaveStorage>, reply: FastifyReply) { | ||||
| 	try { | ||||
| 		const { id } = request.params; | ||||
| 		const { path, newStorage, storageId } = request.body; | ||||
| 		const { hostPath, path, newStorage, storageId } = request.body; | ||||
| 
 | ||||
| 		if (newStorage) { | ||||
| 			await prisma.applicationPersistentStorage.create({ | ||||
| 				data: { path, application: { connect: { id } } } | ||||
| 				data: { hostPath, path, application: { connect: { id } } } | ||||
| 			}); | ||||
| 		} else { | ||||
| 			await prisma.applicationPersistentStorage.update({ | ||||
| 				where: { id: storageId }, | ||||
| 				data: { path } | ||||
| 				data: { hostPath, path } | ||||
| 			}); | ||||
| 		} | ||||
| 		return reply.code(201).send(); | ||||
|  | ||||
| @ -96,6 +96,7 @@ export interface DeleteSecret extends OnlyId { | ||||
| } | ||||
| export interface SaveStorage extends OnlyId { | ||||
| 	Body: { | ||||
| 		hostPath?: string; | ||||
| 		path: string; | ||||
| 		newStorage: boolean; | ||||
| 		storageId: string; | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| 	import { errorNotification } from '$lib/common'; | ||||
| 	import { addToast } from '$lib/store'; | ||||
| 	import CopyVolumeField from '$lib/components/CopyVolumeField.svelte'; | ||||
| 	import SimpleExplainer from '$lib/components/SimpleExplainer.svelte'; | ||||
| 	const { id } = $page.params; | ||||
| 	let isHttps = browser && window.location.protocol === 'https:'; | ||||
| 	export let value: string; | ||||
| @ -33,11 +34,13 @@ | ||||
| 			storage.path.replace(/\/\//g, '/'); | ||||
| 			await post(`/applications/${id}/storages`, { | ||||
| 				path: storage.path, | ||||
| 				hostPath: storage.hostPath, | ||||
| 				storageId: storage.id, | ||||
| 				newStorage | ||||
| 			}); | ||||
| 			dispatch('refresh'); | ||||
| 			if (isNew) { | ||||
| 				storage.hostPath = null; | ||||
| 				storage.path = null; | ||||
| 				storage.id = null; | ||||
| 			} | ||||
| @ -80,27 +83,42 @@ | ||||
| 		<div class="flex gap-4 pb-2" class:pt-8={isNew}> | ||||
| 			{#if storage.applicationId} | ||||
| 				{#if storage.oldPath} | ||||
| 		 | ||||
| 					<CopyVolumeField  | ||||
| 					<CopyVolumeField | ||||
| 						value="{storage.applicationId}{storage.path.replace(/\//gi, '-').replace('-app', '')}" | ||||
| 					/> | ||||
| 				{:else if !storage.hostPath} | ||||
| 					<CopyVolumeField | ||||
| 						value="{storage.applicationId}{storage.path.replace(/\//gi, '-').replace('-app', '')}" | ||||
| 						/> | ||||
| 				{:else} | ||||
| 				 | ||||
| 					<CopyVolumeField  | ||||
| 					value="{storage.applicationId}{storage.path.replace(/\//gi, '-').replace('-app', '')}" | ||||
| 					/> | ||||
| 				{/if} | ||||
| 			{/if} | ||||
| 
 | ||||
| 			{#if isNew} | ||||
| 				<div class="w-full"> | ||||
| 					<input | ||||
| 						disabled={!isNew} | ||||
| 						readonly={!isNew} | ||||
| 						bind:value={storage.hostPath} | ||||
| 						placeholder="Host path, example: ~/.directory" | ||||
| 					/> | ||||
| 
 | ||||
| 					<SimpleExplainer | ||||
| 						text="You can mount <span class='text-yellow-400 font-bold'>host paths</span> from the operating system.<br>Leave it empty to define a volume based volume." | ||||
| 					/> | ||||
| 				</div> | ||||
| 			{:else if storage.hostPath} | ||||
| 				<input disabled readonly value={storage.hostPath} /> | ||||
| 			{/if} | ||||
| 			<input | ||||
| 				disabled={!isNew} | ||||
| 				readonly={!isNew} | ||||
| 				class="w-full" | ||||
| 				bind:value={storage.path} | ||||
| 				required | ||||
| 				placeholder="eg: /data" | ||||
| 				placeholder="Mount point inside the container, example: /data" | ||||
| 			/> | ||||
| 
 | ||||
| 			<div class="flex items-center justify-center"> | ||||
| 			<div class="flex items-start justify-center"> | ||||
| 				{#if isNew} | ||||
| 					<div class="w-full lg:w-64"> | ||||
| 						<button class="btn btn-sm btn-primary w-full" on:click={() => saveStorage(true)} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user