fix: port checker

This commit is contained in:
Andras Bacsai 2022-08-22 19:30:09 +00:00
parent 857e0f251b
commit e755a2d4ec
5 changed files with 52 additions and 36 deletions

View File

@ -35,7 +35,6 @@
"fastify": "4.4.0",
"fastify-plugin": "4.1.0",
"generate-password": "1.7.0",
"get-port": "6.1.2",
"got": "12.3.1",
"is-ip": "5.0.0",
"is-port-reachable": "4.0.0",

View File

@ -17,7 +17,7 @@ import { checkContainer, removeContainer } from './docker';
import { day } from './dayjs';
import * as serviceFields from './serviceFields'
export const version = '3.7.0';
export const version = '3.7.1';
export const isDev = process.env.NODE_ENV === 'development';
const algorithm = 'aes-256-ctr';
@ -545,21 +545,38 @@ export const supportedDatabaseTypesAndVersions = [
}
];
export async function getFreeSSHLocalPort(id: string): Promise<number> {
const { default: getPort, portNumbers } = await import('get-port');
export async function getFreeSSHLocalPort(id: string): Promise<number | boolean> {
const { default: isReachable } = await import('is-port-reachable');
const { remoteIpAddress, sshLocalPort } = await prisma.destinationDocker.findUnique({ where: { id } })
if (sshLocalPort) {
return Number(sshLocalPort)
}
const data = await prisma.setting.findFirst();
const { minPort, maxPort } = data;
const ports = await prisma.destinationDocker.findMany({ where: { sshLocalPort: { not: null }, remoteIpAddress: { not: remoteIpAddress } } })
const alreadyConfigured = await prisma.destinationDocker.findFirst({ where: { remoteIpAddress, id: { not: id }, sshLocalPort: { not: null } } })
const alreadyConfigured = await prisma.destinationDocker.findFirst({
where: {
remoteIpAddress, id: { not: id }, sshLocalPort: { not: null }
}
})
if (alreadyConfigured?.sshLocalPort) {
await prisma.destinationDocker.update({ where: { id }, data: { sshLocalPort: alreadyConfigured.sshLocalPort } })
return Number(alreadyConfigured.sshLocalPort)
}
const availablePort = await getPort({ port: portNumbers(10000, 10100), exclude: ports.map(p => p.sshLocalPort) })
await prisma.destinationDocker.update({ where: { id }, data: { sshLocalPort: Number(availablePort) } })
return Number(availablePort)
const range = generateRangeArray(minPort, maxPort)
console.log({ ports })
const availablePorts = range.filter(port => !ports.map(p => p.sshLocalPort).includes(port))
for (const port of availablePorts) {
const found = await isReachable(port, { host: 'localhost' })
if (!found) {
await prisma.destinationDocker.update({ where: { id }, data: { sshLocalPort: Number(port) } })
return Number(port)
}
}
return false
}
export async function createRemoteEngineConfiguration(id: string) {
@ -1208,7 +1225,7 @@ export async function checkExposedPort({ id, configuredPort, exposePort, dockerI
}
}
export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddress) {
const { default: getPort } = await import('get-port');
const { default: checkPort } = await import('is-port-reachable');
const applicationUsed = await (
await prisma.application.findMany({
where: { exposePort: { not: null }, id: { not: id }, destinationDockerId: dockerId },
@ -1222,22 +1239,23 @@ export async function getFreeExposedPort(id, exposePort, dockerId, remoteIpAddre
})
).map((a) => a.exposePort);
const usedPorts = [...applicationUsed, ...serviceUsed];
if (remoteIpAddress) {
const { default: checkPort } = await import('is-port-reachable');
const found = await checkPort(exposePort, { host: remoteIpAddress });
if (!found) {
return exposePort
}
if (usedPorts.includes(exposePort)) {
return false
}
return await getPort({ port: Number(exposePort), exclude: usedPorts });
const found = await checkPort(exposePort, { host: remoteIpAddress || 'localhost' });
if (!found) {
return exposePort
}
return false
}
export function generateRangeArray(start, end) {
return Array.from({ length: (end - start) }, (v, k) => k + start);
}
export async function getFreePublicPort(id, dockerId) {
const { default: getPort, portNumbers } = await import('get-port');
const { default: isReachable } = await import('is-port-reachable');
const data = await prisma.setting.findFirst();
const { minPort, maxPort } = data;
const dbUsed = await (
await prisma.database.findMany({
where: { publicPort: { not: null }, id: { not: id }, destinationDockerId: dockerId },
@ -1263,7 +1281,15 @@ export async function getFreePublicPort(id, dockerId) {
})
).map((a) => a.publicPort);
const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed];
return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts });
const range = generateRangeArray(minPort, maxPort)
const availablePorts = range.filter(port => !usedPorts.includes(port))
for (const port of availablePorts) {
const found = await isReachable(port, { host: 'localhost' })
if (!found) {
return port
}
}
return false
}
export async function startTraefikTCPProxy(
@ -1645,7 +1671,7 @@ export async function configureServiceType({
}
}
});
} else {
} else {
await prisma.service.update({
where: { id },
data: {

View File

@ -433,9 +433,13 @@ export async function saveDatabaseSettings(request: FastifyRequest<SaveDatabaseS
const { id } = request.params;
const { isPublic, appendOnly = true } = request.body;
const { destinationDocker: { id: dockerId } } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } })
const publicPort = await getFreePublicPort(id, dockerId);
let publicPort = null
const { destinationDocker: { id: dockerId } } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } })
if (isPublic) {
publicPort = await getFreePublicPort(id, dockerId);
}
await prisma.database.update({
where: { id },
data: {

View File

@ -1,7 +1,7 @@
{
"name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "3.7.0",
"version": "3.7.1",
"license": "Apache-2.0",
"repository": "github:coollabsio/coolify",
"scripts": {

13
pnpm-lock.yaml generated
View File

@ -428,7 +428,6 @@ packages:
/@prisma/engines/3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e:
resolution: {integrity: sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==}
requiresBuild: true
dev: true
/@rollup/pluginutils/4.2.1:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
@ -934,7 +933,6 @@ packages:
picocolors: 1.0.0
postcss: 8.4.16
postcss-value-parser: 4.2.0
dev: true
/avvio/8.1.3:
resolution: {integrity: sha512-tl9TC0yDRKzP6gFLkrInqPyx8AkfBC/0QRnwkE9Jo31+OJjLrE/73GJuE0QgSB0Vpv38CTJJZGqU9hczowclWw==}
@ -1735,7 +1733,6 @@ packages:
electron-to-chromium: 1.4.213
node-releases: 2.0.6
update-browserslist-db: 1.0.5_browserslist@4.21.3
dev: true
/bson-objectid/1.3.1:
resolution: {integrity: sha512-eQBNQXsisEAXlwiSy8zRNZdW2xDBJaEVkTPbodYR9hGxxtE548Qq7ilYOd8WAQ86xF7NRUdiWSQ1pa/TkKiE2A==}
@ -1818,7 +1815,6 @@ packages:
/caniuse-lite/1.0.30001375:
resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==}
dev: true
/chalk/1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
@ -2356,7 +2352,6 @@ packages:
/electron-to-chromium/1.4.213:
resolution: {integrity: sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg==}
dev: true
/emoji-regex/8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@ -2848,7 +2843,6 @@ packages:
/escalade/3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
dev: true
/escape-html/1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
@ -3277,7 +3271,6 @@ packages:
/fraction.js/4.2.0:
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
dev: true
/fresh/0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
@ -4321,7 +4314,6 @@ packages:
/node-releases/2.0.6:
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
dev: true
/nodemon/2.0.19:
resolution: {integrity: sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==}
@ -4367,7 +4359,6 @@ packages:
/normalize-range/0.1.2:
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
engines: {node: '>=0.10.0'}
dev: true
/normalize-url/6.1.0:
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
@ -4830,7 +4821,6 @@ packages:
requiresBuild: true
dependencies:
'@prisma/engines': 3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e
dev: true
/private/0.1.8:
resolution: {integrity: sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==}
@ -5616,7 +5606,6 @@ packages:
/svelte/3.49.0:
resolution: {integrity: sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==}
engines: {node: '>= 8'}
dev: true
/sveltekit-i18n/2.2.2_svelte@3.49.0:
resolution: {integrity: sha512-6eygICleGCSL7elY7A3trF8XUhV+mlW56ZSoD0UUKXlw+Y6u0MTTHDq48u1LyY73SfnlbPHXgTarhTjZ0BvUKA==}
@ -5850,7 +5839,6 @@ packages:
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/typpy/2.3.11:
resolution: {integrity: sha512-Jh/fykZSaxeKO0ceMAs6agki9T5TNA9kiIR6fzKbvafKpIw8UlNlHhzuqKyi5lfJJ5VojJOx9tooIbyy7vHV/g==}
@ -5885,7 +5873,6 @@ packages:
browserslist: 4.21.3
escalade: 3.1.1
picocolors: 1.0.0
dev: true
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}