feat: remote docker engine init
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { asyncExecShell, errorHandler, listSettings, prisma, startCoolifyProxy, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||
import sshConfig from 'ssh-config'
|
||||
import fs from 'fs/promises'
|
||||
import { asyncExecShell, decrypt, errorHandler, listSettings, prisma, startCoolifyProxy, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common';
|
||||
import { checkContainer, dockerInstance, getEngine } from '../../../../lib/docker';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
@@ -44,7 +46,8 @@ export async function getDestination(request: FastifyRequest<OnlyId>) {
|
||||
const { id } = request.params
|
||||
const teamId = request.user?.teamId;
|
||||
const destination = await prisma.destinationDocker.findFirst({
|
||||
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }
|
||||
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||
include: { sshKey: true }
|
||||
});
|
||||
if (!destination && id !== 'new') {
|
||||
throw { status: 404, message: `Destination not found.` };
|
||||
@@ -80,39 +83,51 @@ export async function getDestination(request: FastifyRequest<OnlyId>) {
|
||||
export async function newDestination(request: FastifyRequest<NewDestination>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params
|
||||
let { name, network, engine, isCoolifyProxyUsed } = request.body
|
||||
let { name, network, engine, isCoolifyProxyUsed, ipAddress, user, port, sshPrivateKey } = request.body
|
||||
const teamId = request.user.teamId;
|
||||
if (id === 'new') {
|
||||
const host = getEngine(engine);
|
||||
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
||||
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
||||
if (found.length === 0) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
}
|
||||
await prisma.destinationDocker.create({
|
||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed }
|
||||
});
|
||||
const destinations = await prisma.destinationDocker.findMany({ where: { engine } });
|
||||
const destination = destinations.find((destination) => destination.network === network);
|
||||
if (engine) {
|
||||
const host = getEngine(engine);
|
||||
const docker = dockerInstance({ destinationDocker: { engine, network } });
|
||||
const found = await docker.engine.listNetworks({ filters: { name: [`^${network}$`] } });
|
||||
if (found.length === 0) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
}
|
||||
await prisma.destinationDocker.create({
|
||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed }
|
||||
});
|
||||
const destinations = await prisma.destinationDocker.findMany({ where: { engine } });
|
||||
const destination = destinations.find((destination) => destination.network === network);
|
||||
|
||||
if (destinations.length > 0) {
|
||||
const proxyConfigured = destinations.find(
|
||||
(destination) => destination.network !== network && destination.isCoolifyProxyUsed === true
|
||||
);
|
||||
if (proxyConfigured) {
|
||||
isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed;
|
||||
if (destinations.length > 0) {
|
||||
const proxyConfigured = destinations.find(
|
||||
(destination) => destination.network !== network && destination.isCoolifyProxyUsed === true
|
||||
);
|
||||
if (proxyConfigured) {
|
||||
isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed;
|
||||
}
|
||||
await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
|
||||
}
|
||||
await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } });
|
||||
}
|
||||
if (isCoolifyProxyUsed) {
|
||||
const settings = await prisma.setting.findFirst();
|
||||
if (settings?.isTraefikUsed) {
|
||||
await startTraefikProxy(engine);
|
||||
} else {
|
||||
await startCoolifyProxy(engine);
|
||||
if (isCoolifyProxyUsed) {
|
||||
const settings = await prisma.setting.findFirst();
|
||||
if (settings?.isTraefikUsed) {
|
||||
await startTraefikProxy(engine);
|
||||
} else {
|
||||
await startCoolifyProxy(engine);
|
||||
}
|
||||
}
|
||||
return reply.code(201).send({ id: destination.id });
|
||||
}
|
||||
return reply.code(201).send({ id: destination.id });
|
||||
if (ipAddress) {
|
||||
await prisma.destinationDocker.create({
|
||||
data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed, remoteEngine: true, remoteIpAddress: ipAddress, remoteUser: user, remotePort: port }
|
||||
});
|
||||
return reply.code(201).send()
|
||||
|
||||
}
|
||||
throw {
|
||||
message: `Cannot save Docker Engine.`
|
||||
};
|
||||
} else {
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } });
|
||||
return reply.code(201).send();
|
||||
@@ -120,6 +135,8 @@ export async function newDestination(request: FastifyRequest<NewDestination>, re
|
||||
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
} finally {
|
||||
await fs.rm('./id_rsa')
|
||||
}
|
||||
}
|
||||
export async function deleteDestination(request: FastifyRequest<OnlyId>) {
|
||||
@@ -195,3 +212,45 @@ export async function restartProxy(request: FastifyRequest<Proxy>) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function assignSSHKey(request: FastifyRequest) {
|
||||
try {
|
||||
const { id: sshKeyId } = request.body;
|
||||
const { id } = request.params;
|
||||
console.log({ id, sshKeyId })
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { sshKey: { connect: { id: sshKeyId } } } })
|
||||
return {}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function verifyRemoteDockerEngine(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { sshKey: { privateKey }, remoteIpAddress, remotePort, remoteUser, network } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } })
|
||||
await fs.writeFile('./id_rsa', decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 })
|
||||
const host = `ssh://${remoteUser}@${remoteIpAddress}`
|
||||
const config = sshConfig.parse('')
|
||||
const found = config.find({ Host: remoteIpAddress })
|
||||
if (!found) {
|
||||
config.append({
|
||||
Host: remoteIpAddress,
|
||||
Port: remotePort.toString(),
|
||||
User: remoteUser,
|
||||
IdentityFile: '/workspace/coolify/apps/api/id_rsa',
|
||||
StrictHostKeyChecking: 'no'
|
||||
})
|
||||
}
|
||||
await fs.writeFile('/home/gitpod/.ssh/config', sshConfig.stringify(config))
|
||||
const { stdout } = await asyncExecShell(`DOCKER_HOST=${host} docker network ls --filter 'name=${network}' --no-trunc --format "{{json .}}"`);
|
||||
console.log({ stdout })
|
||||
if (!stdout) {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker network create --attachable ${network}`);
|
||||
}
|
||||
await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } })
|
||||
return reply.code(201).send()
|
||||
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkDestination, deleteDestination, getDestination, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy } from './handlers';
|
||||
import { assignSSHKey, checkDestination, deleteDestination, getDestination, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy, verifyRemoteDockerEngine } from './handlers';
|
||||
|
||||
import type { OnlyId } from '../../../../types';
|
||||
import type { CheckDestination, NewDestination, Proxy, SaveDestinationSettings } from './types';
|
||||
@@ -15,10 +15,14 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
fastify.post<NewDestination>('/:id', async (request, reply) => await newDestination(request, reply));
|
||||
fastify.delete<OnlyId>('/:id', async (request) => await deleteDestination(request));
|
||||
|
||||
fastify.post<SaveDestinationSettings>('/:id/settings', async (request, reply) => await saveDestinationSettings(request));
|
||||
fastify.post<Proxy>('/:id/start', async (request, reply) => await startProxy(request));
|
||||
fastify.post<Proxy>('/:id/stop', async (request, reply) => await stopProxy(request));
|
||||
fastify.post<Proxy>('/:id/restart', async (request, reply) => await restartProxy(request));
|
||||
fastify.post<SaveDestinationSettings>('/:id/settings', async (request) => await saveDestinationSettings(request));
|
||||
fastify.post<Proxy>('/:id/start', async (request,) => await startProxy(request));
|
||||
fastify.post<Proxy>('/:id/stop', async (request) => await stopProxy(request));
|
||||
fastify.post<Proxy>('/:id/restart', async (request) => await restartProxy(request));
|
||||
|
||||
fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request));
|
||||
|
||||
fastify.post('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
@@ -1,15 +1,23 @@
|
||||
import { promises as dns } from 'dns';
|
||||
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkDomainsIsValidInDNS, errorHandler, getDomain, isDNSValid, isDomainConfigured, listSettings, prisma } from '../../../../lib/common';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, SaveSettings } from './types';
|
||||
import { checkDomainsIsValidInDNS, decrypt, encrypt, errorHandler, getDomain, isDNSValid, isDomainConfigured, listSettings, prisma } from '../../../../lib/common';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, SaveSettings, SaveSSHKey } from './types';
|
||||
|
||||
|
||||
export async function listAllSettings(request: FastifyRequest) {
|
||||
try {
|
||||
const settings = await listSettings();
|
||||
const sshKeys = await prisma.sshKey.findMany()
|
||||
const unencryptedKeys = []
|
||||
if (sshKeys.length > 0) {
|
||||
for (const key of sshKeys) {
|
||||
unencryptedKeys.push({ id: key.id, name: key.name, privateKey: decrypt(key.privateKey), createdAt: key.createdAt })
|
||||
}
|
||||
}
|
||||
return {
|
||||
settings
|
||||
settings,
|
||||
sshKeys: unencryptedKeys
|
||||
}
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
@@ -83,4 +91,30 @@ export async function checkDNS(request: FastifyRequest<CheckDNS>) {
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveSSHKey(request: FastifyRequest<SaveSSHKey>, reply: FastifyReply) {
|
||||
try {
|
||||
const { privateKey, name } = request.body;
|
||||
const found = await prisma.sshKey.findMany({ where: { name } })
|
||||
if (found.length > 0) {
|
||||
throw {
|
||||
message: "Name already used. Choose another one please."
|
||||
}
|
||||
}
|
||||
const encryptedSSHKey = encrypt(privateKey)
|
||||
await prisma.sshKey.create({ data: { name, privateKey: encryptedSSHKey } })
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
||||
export async function deleteSSHKey(request: FastifyRequest<DeleteSSHKey>, reply: FastifyReply) {
|
||||
try {
|
||||
const { id } = request.body;
|
||||
await prisma.sshKey.delete({ where: { id } })
|
||||
return reply.code(201).send()
|
||||
} catch ({ status, message }) {
|
||||
return errorHandler({ status, message })
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { FastifyPluginAsync } from 'fastify';
|
||||
import { checkDNS, checkDomain, deleteDomain, listAllSettings, saveSettings } from './handlers';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, SaveSettings } from './types';
|
||||
import { checkDNS, checkDomain, deleteDomain, deleteSSHKey, listAllSettings, saveSettings, saveSSHKey } from './handlers';
|
||||
import { CheckDNS, CheckDomain, DeleteDomain, DeleteSSHKey, SaveSettings, SaveSSHKey } from './types';
|
||||
|
||||
|
||||
const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
@@ -13,6 +13,9 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
||||
|
||||
fastify.get<CheckDNS>('/check', async (request) => await checkDNS(request));
|
||||
fastify.post<CheckDomain>('/check', async (request) => await checkDomain(request));
|
||||
|
||||
fastify.post<SaveSSHKey>('/sshKey', async (request, reply) => await saveSSHKey(request, reply));
|
||||
fastify.delete<DeleteSSHKey>('/sshKey', async (request, reply) => await deleteSSHKey(request, reply));
|
||||
};
|
||||
|
||||
export default root;
|
||||
|
@@ -28,4 +28,15 @@ export interface CheckDNS {
|
||||
Params: {
|
||||
domain: string,
|
||||
}
|
||||
}
|
||||
export interface SaveSSHKey {
|
||||
Body: {
|
||||
privateKey: string,
|
||||
name: string
|
||||
}
|
||||
}
|
||||
export interface DeleteSSHKey {
|
||||
Body: {
|
||||
id: string
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user