WIP: Remote docker engine
This commit is contained in:
parent
c9b52f1310
commit
e0e50b4bd5
@ -147,8 +147,12 @@ model DestinationDocker {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
network String @unique
|
network String @unique
|
||||||
name String
|
name String
|
||||||
engine String
|
engine String?
|
||||||
remoteEngine Boolean @default(false)
|
remoteEngine Boolean @default(false)
|
||||||
|
ipAddress String?
|
||||||
|
sshPrivateKey String?
|
||||||
|
user String? @default("root")
|
||||||
|
port Int? @default(22)
|
||||||
isCoolifyProxyUsed Boolean? @default(false)
|
isCoolifyProxyUsed Boolean? @default(false)
|
||||||
teams Team[]
|
teams Team[]
|
||||||
application Application[]
|
application Application[]
|
||||||
|
@ -103,7 +103,7 @@ export const getUserDetails = async (event, isAdminRequired = true) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getEngine(engine) {
|
export function getEngine(engine) {
|
||||||
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : `tcp://${engine}:2375`;
|
return engine === '/var/run/docker.sock' ? 'unix:///var/run/docker.sock' : engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeContainer(id, engine) {
|
export async function removeContainer(id, engine) {
|
||||||
|
@ -15,3 +15,6 @@ export const notNodeDeployments = ['php', 'docker', 'rust'];
|
|||||||
export function getDomain(domain) {
|
export function getDomain(domain) {
|
||||||
return domain?.replace('https://', '').replace('http://', '');
|
return domain?.replace('https://', '').replace('http://', '');
|
||||||
}
|
}
|
||||||
|
export function generateRemoteEngine(destination) {
|
||||||
|
return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { asyncExecShell, getEngine } from '$lib/common';
|
import { asyncExecShell, getEngine } from '$lib/common';
|
||||||
|
import { decrypt, encrypt } from '$lib/crypto';
|
||||||
import { dockerInstance } from '$lib/docker';
|
import { dockerInstance } from '$lib/docker';
|
||||||
import { startCoolifyProxy } from '$lib/haproxy';
|
import { startCoolifyProxy } from '$lib/haproxy';
|
||||||
import { getDatabaseImage } from '.';
|
import { getDatabaseImage } from '.';
|
||||||
@ -47,7 +48,36 @@ export async function updateDestination({ id, name, engine, network }) {
|
|||||||
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
return await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function newDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
export async function newRemoteDestination({
|
||||||
|
name,
|
||||||
|
teamId,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
}) {
|
||||||
|
const encryptedPrivateKey = encrypt(sshPrivateKey);
|
||||||
|
const destination = await prisma.destinationDocker.create({
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
teams: { connect: { id: teamId } },
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey: encryptedPrivateKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return destination.id;
|
||||||
|
}
|
||||||
|
export async function newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed }) {
|
||||||
const host = getEngine(engine);
|
const host = getEngine(engine);
|
||||||
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
||||||
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
||||||
@ -94,9 +124,13 @@ export async function removeDestination({ id }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getDestination({ id, teamId }) {
|
export async function getDestination({ id, teamId }) {
|
||||||
return await prisma.destinationDocker.findFirst({
|
let destination = await prisma.destinationDocker.findFirst({
|
||||||
where: { id, teams: { some: { id: teamId } } }
|
where: { id, teams: { some: { id: teamId } } }
|
||||||
});
|
});
|
||||||
|
if (destination.remoteEngine) {
|
||||||
|
destination.sshPrivateKey = decrypt(destination.sshPrivateKey);
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
}
|
}
|
||||||
export async function getDestinationByApplicationId({ id, teamId }) {
|
export async function getDestinationByApplicationId({ id, teamId }) {
|
||||||
return await prisma.destinationDocker.findFirst({
|
return await prisma.destinationDocker.findFirst({
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
export let state;
|
export let state;
|
||||||
|
|
||||||
import { toast } from '@zerodevx/svelte-toast';
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
import { page } from '$app/stores';
|
import { page, session } from '$app/stores';
|
||||||
import Setting from '$lib/components/Setting.svelte';
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
import { errorNotification } from '$lib/form';
|
import { errorNotification } from '$lib/form';
|
||||||
import { post } from '$lib/api';
|
import { post } from '$lib/api';
|
||||||
@ -125,27 +125,35 @@
|
|||||||
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
<div class="flex space-x-1 pb-5">
|
<div class="flex space-x-1 pb-5">
|
||||||
<div class="title font-bold">Configuration</div>
|
<div class="title font-bold">Configuration</div>
|
||||||
<button
|
{#if $session.isAdmin}
|
||||||
type="submit"
|
<button
|
||||||
class="bg-sky-600 hover:bg-sky-500"
|
type="submit"
|
||||||
class:bg-sky-600={!loading}
|
class="bg-sky-600 hover:bg-sky-500"
|
||||||
class:hover:bg-sky-500={!loading}
|
class:bg-sky-600={!loading}
|
||||||
disabled={loading}
|
class:hover:bg-sky-500={!loading}
|
||||||
>{loading ? 'Saving...' : 'Save'}
|
disabled={loading}
|
||||||
</button>
|
>{loading ? 'Saving...' : 'Save'}
|
||||||
<button
|
</button>
|
||||||
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
<button
|
||||||
disabled={restarting}
|
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
||||||
on:click|preventDefault={forceRestartProxy}
|
disabled={restarting}
|
||||||
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
on:click|preventDefault={forceRestartProxy}
|
||||||
>
|
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||||
>Scan for applications</button
|
>Scan for applications</button
|
||||||
> -->
|
> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center px-10 ">
|
<div class="grid grid-cols-2 items-center px-10 ">
|
||||||
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
<input name="name" placeholder="name" bind:value={destination.name} />
|
<input
|
||||||
|
name="name"
|
||||||
|
placeholder="name"
|
||||||
|
disabled={!$session.isAdmin}
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
bind:value={destination.name}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
225
src/routes/destinations/[id]/_RemoteDocker.svelte
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let destination;
|
||||||
|
export let settings;
|
||||||
|
export let state;
|
||||||
|
|
||||||
|
import { toast } from '@zerodevx/svelte-toast';
|
||||||
|
import { page, session } from '$app/stores';
|
||||||
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { generateRemoteEngine } from '$lib/components/common';
|
||||||
|
const { id } = $page.params;
|
||||||
|
let cannotDisable = settings.fqdn && destination.engine === '/var/run/docker.sock';
|
||||||
|
// let scannedApps = [];
|
||||||
|
let loading = false;
|
||||||
|
let restarting = false;
|
||||||
|
async function handleSubmit() {
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
return await post(`/destinations/${id}.json`, { ...destination });
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// async function scanApps() {
|
||||||
|
// scannedApps = [];
|
||||||
|
// const data = await fetch(`/destinations/${id}/scan.json`);
|
||||||
|
// const { containers } = await data.json();
|
||||||
|
// scannedApps = containers;
|
||||||
|
// }
|
||||||
|
onMount(async () => {
|
||||||
|
if (state === false && destination.isCoolifyProxyUsed === true) {
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
await stopProxy();
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
} else if (state === true && destination.isCoolifyProxyUsed === false) {
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
await startProxy();
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
async function changeProxySetting() {
|
||||||
|
if (!cannotDisable) {
|
||||||
|
const isProxyActivated = destination.isCoolifyProxyUsed;
|
||||||
|
if (isProxyActivated) {
|
||||||
|
const sure = confirm(
|
||||||
|
`Are you sure you want to ${
|
||||||
|
destination.isCoolifyProxyUsed ? 'disable' : 'enable'
|
||||||
|
} Coolify proxy? It will remove the proxy for all configured networks and all deployments on '${
|
||||||
|
destination.engine
|
||||||
|
}'! Nothing will be reachable if you do it!`
|
||||||
|
);
|
||||||
|
if (!sure) return;
|
||||||
|
}
|
||||||
|
destination.isCoolifyProxyUsed = !destination.isCoolifyProxyUsed;
|
||||||
|
try {
|
||||||
|
await post(`/destinations/${id}/settings.json`, {
|
||||||
|
isCoolifyProxyUsed: destination.isCoolifyProxyUsed,
|
||||||
|
engine: destination.engine
|
||||||
|
});
|
||||||
|
if (isProxyActivated) {
|
||||||
|
await stopProxy();
|
||||||
|
} else {
|
||||||
|
await startProxy();
|
||||||
|
}
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function stopProxy() {
|
||||||
|
try {
|
||||||
|
const engine = generateRemoteEngine(destination);
|
||||||
|
await post(`/destinations/${id}/stop.json`, { engine });
|
||||||
|
return toast.push('Coolify Proxy stopped!');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function startProxy() {
|
||||||
|
try {
|
||||||
|
const engine = generateRemoteEngine(destination);
|
||||||
|
await post(`/destinations/${id}/start.json`, { engine });
|
||||||
|
return toast.push('Coolify Proxy started!');
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function forceRestartProxy() {
|
||||||
|
const sure = confirm(
|
||||||
|
'Are you sure you want to restart the proxy? Everyting will be reconfigured in ~10 sec.'
|
||||||
|
);
|
||||||
|
if (sure) {
|
||||||
|
try {
|
||||||
|
restarting = true;
|
||||||
|
toast.push('Coolify Proxy restarting...');
|
||||||
|
await post(`/destinations/${id}/restart.json`, {
|
||||||
|
engine: destination.engine,
|
||||||
|
fqdn: settings.fqdn
|
||||||
|
});
|
||||||
|
} catch ({ error }) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
|
<div class="flex space-x-1 pb-5">
|
||||||
|
<div class="title font-bold">Configuration</div>
|
||||||
|
{#if $session.isAdmin}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="bg-sky-600 hover:bg-sky-500"
|
||||||
|
class:bg-sky-600={!loading}
|
||||||
|
class:hover:bg-sky-500={!loading}
|
||||||
|
disabled={loading}
|
||||||
|
>{loading ? 'Saving...' : 'Save'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class={restarting ? '' : 'bg-red-600 hover:bg-red-500'}
|
||||||
|
disabled={restarting}
|
||||||
|
on:click|preventDefault={forceRestartProxy}
|
||||||
|
>{restarting ? 'Restarting... please wait...' : 'Force restart proxy'}</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<!-- <button type="button" class="bg-coollabs hover:bg-coollabs-100" on:click={scanApps}
|
||||||
|
>Scan for applications</button
|
||||||
|
> -->
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10 ">
|
||||||
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
|
<input
|
||||||
|
name="name"
|
||||||
|
placeholder="name"
|
||||||
|
disabled={!$session.isAdmin}
|
||||||
|
readonly={!$session.isAdmin}
|
||||||
|
bind:value={destination.name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="engine" class="text-base font-bold text-stone-100">Engine</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="engine"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="engine"
|
||||||
|
placeholder="eg: /var/run/docker.sock"
|
||||||
|
value={destination.engine}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="flex items-center">
|
||||||
|
<label for="remoteEngine">Remote Engine?</label>
|
||||||
|
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
||||||
|
</div> -->
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
|
<CopyPasswordField
|
||||||
|
id="network"
|
||||||
|
readonly
|
||||||
|
disabled
|
||||||
|
name="network"
|
||||||
|
placeholder="default: coolify"
|
||||||
|
value={destination.network}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<Setting
|
||||||
|
disabled={cannotDisable}
|
||||||
|
bind:setting={destination.isCoolifyProxyUsed}
|
||||||
|
on:click={changeProxySetting}
|
||||||
|
title="Use Coolify Proxy?"
|
||||||
|
description={`This will install a proxy on the destination to allow you to access your applications and services without any manual configuration. Databases will have their own proxy. <br><br>${
|
||||||
|
cannotDisable
|
||||||
|
? '<span class="font-bold text-white">You cannot disable this proxy as FQDN is configured for Coolify.</span>'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<!-- <div class="flex justify-center">
|
||||||
|
{#if payload.isCoolifyProxyUsed}
|
||||||
|
{#if state}
|
||||||
|
<button on:click={stopProxy}>Stop proxy</button>
|
||||||
|
{:else}
|
||||||
|
<button on:click={startProxy}>Start proxy</button>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- {#if scannedApps.length > 0}
|
||||||
|
<div class="flex justify-center px-6 pb-10">
|
||||||
|
<div class="flex space-x-2 h-8 items-center">
|
||||||
|
<div class="font-bold text-xl text-white">Found applications</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="max-w-4xl mx-auto px-6">
|
||||||
|
<div class="flex space-x-2 justify-center">
|
||||||
|
{#each scannedApps as app}
|
||||||
|
<FoundApp {app} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if} -->
|
@ -1,4 +1,5 @@
|
|||||||
import { asyncExecShell, getEngine, getTeam, getUserDetails } from '$lib/common';
|
import { getUserDetails } from '$lib/common';
|
||||||
|
import { generateRemoteEngine } from '$lib/components/common';
|
||||||
import * as db from '$lib/database';
|
import * as db from '$lib/database';
|
||||||
import { ErrorHandler } from '$lib/database';
|
import { ErrorHandler } from '$lib/database';
|
||||||
import { checkContainer } from '$lib/haproxy';
|
import { checkContainer } from '$lib/haproxy';
|
||||||
@ -12,15 +13,21 @@ export const get: RequestHandler = async (event) => {
|
|||||||
try {
|
try {
|
||||||
const destination = await db.getDestination({ id, teamId });
|
const destination = await db.getDestination({ id, teamId });
|
||||||
const settings = await db.listSettings();
|
const settings = await db.listSettings();
|
||||||
const state =
|
let payload = {
|
||||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
destination,
|
||||||
|
settings,
|
||||||
|
state: false
|
||||||
|
};
|
||||||
|
if (destination.remoteEngine) {
|
||||||
|
const engine = await generateRemoteEngine(destination);
|
||||||
|
payload.state = await checkContainer(engine, 'coolify-haproxy');
|
||||||
|
} else {
|
||||||
|
payload.state =
|
||||||
|
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status: 200,
|
||||||
body: {
|
body: { ...payload }
|
||||||
destination,
|
|
||||||
settings,
|
|
||||||
state
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
import type Prisma from '@prisma/client';
|
import type Prisma from '@prisma/client';
|
||||||
import LocalDocker from './_LocalDocker.svelte';
|
import LocalDocker from './_LocalDocker.svelte';
|
||||||
|
import RemoteDocker from './_RemoteDocker.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 p-6 text-2xl font-bold">
|
<div class="flex space-x-1 p-6 text-2xl font-bold">
|
||||||
@ -42,6 +43,11 @@
|
|||||||
<span class="arrow-right-applications px-1">></span>
|
<span class="arrow-right-applications px-1">></span>
|
||||||
<span class="pr-2">{destination.name}</span>
|
<span class="pr-2">{destination.name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mx-auto max-w-4xl px-6">
|
<div class="mx-auto max-w-4xl px-6">
|
||||||
<LocalDocker bind:destination {settings} {state} />
|
{#if destination.remoteEngine}
|
||||||
|
<RemoteDocker bind:destination {settings} {state} />
|
||||||
|
{:else}
|
||||||
|
<LocalDocker bind:destination {settings} {state} />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,26 +51,7 @@
|
|||||||
placeholder="eg: /var/run/docker.sock"
|
placeholder="eg: /var/run/docker.sock"
|
||||||
bind:value={payload.engine}
|
bind:value={payload.engine}
|
||||||
/>
|
/>
|
||||||
<!-- <Explainer text="You can use remote Docker Engine with over SSH." /> -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="flex items-center">
|
|
||||||
<label for="remoteEngine">Remote Docker Engine?</label>
|
|
||||||
<input name="remoteEngine" type="checkbox" bind:checked={payload.remoteEngine} />
|
|
||||||
</div>
|
|
||||||
{#if payload.remoteEngine}
|
|
||||||
<div class="grid grid-cols-3 items-center">
|
|
||||||
<label for="user">User</label>
|
|
||||||
<div class="col-span-2">
|
|
||||||
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-3 items-center">
|
|
||||||
<label for="port">Port</label>
|
|
||||||
<div class="col-span-2">
|
|
||||||
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if} -->
|
|
||||||
<div class="grid grid-cols-2 items-center px-10">
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
90
src/routes/new/destination/_RemoteDocker.svelte
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
export let payload;
|
||||||
|
|
||||||
|
import { post } from '$lib/api';
|
||||||
|
import Explainer from '$lib/components/Explainer.svelte';
|
||||||
|
import Setting from '$lib/components/Setting.svelte';
|
||||||
|
import { errorNotification } from '$lib/form';
|
||||||
|
|
||||||
|
let loading = false;
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
const { id } = await post('/new/destination/docker.json', {
|
||||||
|
...payload
|
||||||
|
});
|
||||||
|
return await goto(`/destinations/${id}`);
|
||||||
|
} catch ({ error }) {
|
||||||
|
return errorNotification(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex justify-center px-6 pb-8">
|
||||||
|
<form on:submit|preventDefault={handleSubmit} class="grid grid-flow-row gap-2 py-4">
|
||||||
|
<div class="flex items-center space-x-2 pb-5">
|
||||||
|
<div class="title font-bold">Configuration</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class:bg-sky-600={!loading}
|
||||||
|
class:hover:bg-sky-500={!loading}
|
||||||
|
disabled={loading}
|
||||||
|
>{loading
|
||||||
|
? payload.isCoolifyProxyUsed
|
||||||
|
? 'Saving and configuring proxy...'
|
||||||
|
: 'Saving...'
|
||||||
|
: 'Save'}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="name" class="text-base font-bold text-stone-100">Name</label>
|
||||||
|
<input required name="name" placeholder="name" bind:value={payload.name} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="ipAddress" class="text-base font-bold text-stone-100">IP Address</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
name="ipAddress"
|
||||||
|
placeholder="eg: 192.168..."
|
||||||
|
bind:value={payload.ipAddress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="user" class="text-base font-bold text-stone-100">User</label>
|
||||||
|
<input required name="user" placeholder="eg: root" bind:value={payload.user} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="port" class="text-base font-bold text-stone-100">Port</label>
|
||||||
|
<input required name="port" placeholder="eg: 22" bind:value={payload.port} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="sshPrivateKey" class="text-base font-bold text-stone-100">SSH Private Key</label>
|
||||||
|
<textarea
|
||||||
|
rows="10"
|
||||||
|
class="resize-none"
|
||||||
|
required
|
||||||
|
name="sshPrivateKey"
|
||||||
|
placeholder="eg: -----BEGIN...."
|
||||||
|
bind:value={payload.sshPrivateKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 items-center px-10">
|
||||||
|
<label for="network" class="text-base font-bold text-stone-100">Network</label>
|
||||||
|
<input required name="network" placeholder="default: coolify" bind:value={payload.network} />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<Setting
|
||||||
|
bind:setting={payload.isCoolifyProxyUsed}
|
||||||
|
on:click={() => (payload.isCoolifyProxyUsed = !payload.isCoolifyProxyUsed)}
|
||||||
|
title="Use Coolify Proxy?"
|
||||||
|
description="This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).<br><br>Databases will have their own proxy."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -8,10 +8,36 @@ export const post: RequestHandler = async (event) => {
|
|||||||
const { teamId, status, body } = await getUserDetails(event);
|
const { teamId, status, body } = await getUserDetails(event);
|
||||||
if (status === 401) return { status, body };
|
if (status === 401) return { status, body };
|
||||||
|
|
||||||
const { name, engine, network, isCoolifyProxyUsed } = await event.request.json();
|
const {
|
||||||
|
name,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
} = await event.request.json();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const id = await db.newDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
let id = null;
|
||||||
|
if (remoteEngine) {
|
||||||
|
id = await db.newRemoteDestination({
|
||||||
|
name,
|
||||||
|
teamId,
|
||||||
|
engine,
|
||||||
|
network,
|
||||||
|
isCoolifyProxyUsed,
|
||||||
|
remoteEngine,
|
||||||
|
ipAddress,
|
||||||
|
user,
|
||||||
|
port,
|
||||||
|
sshPrivateKey
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
id = await db.newLocalDestination({ name, teamId, engine, network, isCoolifyProxyUsed });
|
||||||
|
}
|
||||||
return { status: 200, body: { id } };
|
return { status: 200, body: { id } };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorHandler(error);
|
return ErrorHandler(error);
|
||||||
|
@ -1,25 +1,34 @@
|
|||||||
<script>
|
<script>
|
||||||
import Docker from './_Docker.svelte';
|
import LocalDocker from './_LocalDocker.svelte';
|
||||||
import cuid from 'cuid';
|
import cuid from 'cuid';
|
||||||
|
import RemoteDocker from './_RemoteDocker.svelte';
|
||||||
let payload = {};
|
let payload = {};
|
||||||
let selected = 'docker';
|
let selected = 'localDocker';
|
||||||
|
|
||||||
function setPredefined(type) {
|
function setPredefined(type) {
|
||||||
selected = type;
|
selected = type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'docker':
|
case 'localDocker':
|
||||||
payload = {
|
payload = {
|
||||||
name: 'Local Docker',
|
name: 'Local Docker',
|
||||||
engine: '/var/run/docker.sock',
|
engine: '/var/run/docker.sock',
|
||||||
remoteEngine: false,
|
remoteEngine: false,
|
||||||
user: 'root',
|
|
||||||
port: 22,
|
|
||||||
privateKey: null,
|
|
||||||
network: cuid(),
|
network: cuid(),
|
||||||
isCoolifyProxyUsed: true
|
isCoolifyProxyUsed: true
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case 'remoteDocker':
|
||||||
|
payload = {
|
||||||
|
name: 'Remote Docker',
|
||||||
|
remoteEngine: true,
|
||||||
|
ipAddress: null,
|
||||||
|
user: 'root',
|
||||||
|
port: 22,
|
||||||
|
sshPrivateKey: null,
|
||||||
|
network: cuid(),
|
||||||
|
isCoolifyProxyUsed: true
|
||||||
|
};
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -32,12 +41,15 @@
|
|||||||
<div class="flex-col space-y-2 pb-10 text-center">
|
<div class="flex-col space-y-2 pb-10 text-center">
|
||||||
<div class="text-xl font-bold text-white">Predefined destinations</div>
|
<div class="text-xl font-bold text-white">Predefined destinations</div>
|
||||||
<div class="flex justify-center space-x-2">
|
<div class="flex justify-center space-x-2">
|
||||||
<button class="w-32" on:click={() => setPredefined('docker')}>Docker</button>
|
<button class="w-32" on:click={() => setPredefined('localDocker')}>Local Docker</button>
|
||||||
|
<button class="w-32" on:click={() => setPredefined('remoteDocker')}>Remote Docker</button>
|
||||||
<button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button>
|
<button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if selected === 'docker'}
|
{#if selected === 'localDocker'}
|
||||||
<Docker {payload} />
|
<LocalDocker {payload} />
|
||||||
|
{:else if selected === 'remoteDocker'}
|
||||||
|
<RemoteDocker {payload} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-center font-bold text-4xl py-10">Not implemented yet</div>
|
<div class="text-center font-bold text-4xl py-10">Not implemented yet</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
Reference in New Issue
Block a user