diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index 541fb8685..47a8c3f9a 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -9,6 +9,7 @@ import { default as ProdPrisma } from '@prisma/client'; import type { Database, DatabaseSettings } from '@prisma/client'; import generator from 'generate-password'; import forge from 'node-forge'; +import getPort, { portNumbers } from 'get-port'; export function generatePassword(length = 24): string { return generator.generate({ @@ -251,3 +252,29 @@ export function generateDatabaseConfiguration(database: Database & { settings: D }; } } + +export async function getFreePort() { + const data = await prisma.setting.findFirst(); + const { minPort, maxPort } = data; + + const dbUsed = await ( + await prisma.database.findMany({ + where: { publicPort: { not: null } }, + select: { publicPort: true } + }) + ).map((a) => a.publicPort); + const wpFtpUsed = await ( + await prisma.wordpress.findMany({ + where: { ftpPublicPort: { not: null } }, + select: { ftpPublicPort: true } + }) + ).map((a) => a.ftpPublicPort); + const wpUsed = await ( + await prisma.wordpress.findMany({ + where: { mysqlPublicPort: { not: null } }, + select: { mysqlPublicPort: true } + }) + ).map((a) => a.mysqlPublicPort); + const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed]; + return await getPort({ port: portNumbers(minPort, maxPort), exclude: usedPorts }); +} diff --git a/src/lib/queues/index.ts b/src/lib/queues/index.ts index 694ddc9cc..84d5f7cbc 100644 --- a/src/lib/queues/index.ts +++ b/src/lib/queues/index.ts @@ -7,6 +7,7 @@ import builder from './builder'; import logger from './logger'; import cleanup from './cleanup'; import proxy from './proxy'; +import proxyTcpHttp from './proxyTcpHttp'; import ssl from './ssl'; import sslrenewal from './sslrenewal'; @@ -29,17 +30,20 @@ const connectionOptions = { const cron = async (): Promise => { new QueueScheduler('proxy', connectionOptions); + new QueueScheduler('proxyTcpHttp', connectionOptions); new QueueScheduler('cleanup', connectionOptions); new QueueScheduler('ssl', connectionOptions); new QueueScheduler('sslRenew', connectionOptions); const queue = { proxy: new Queue('proxy', { ...connectionOptions }), + proxyTcpHttp: new Queue('proxyTcpHttp', { ...connectionOptions }), cleanup: new Queue('cleanup', { ...connectionOptions }), ssl: new Queue('ssl', { ...connectionOptions }), sslRenew: new Queue('sslRenew', { ...connectionOptions }) }; await queue.proxy.drain(); + await queue.proxyTcpHttp.drain(); await queue.cleanup.drain(); await queue.ssl.drain(); await queue.sslRenew.drain(); @@ -54,6 +58,16 @@ const cron = async (): Promise => { } ); + new Worker( + 'proxyTcpHttp', + async () => { + await proxyTcpHttp(); + }, + { + ...connectionOptions + } + ); + new Worker( 'ssl', async () => { @@ -85,6 +99,7 @@ const cron = async (): Promise => { ); await queue.proxy.add('proxy', {}, { repeat: { every: 10000 } }); + await queue.proxyTcpHttp.add('proxyTcpHttp', {}, { repeat: { every: 10000 } }); await queue.ssl.add('ssl', {}, { repeat: { every: dev ? 10000 : 60000 } }); if (!dev) await queue.cleanup.add('cleanup', {}, { repeat: { every: 300000 } }); await queue.sslRenew.add('sslRenew', {}, { repeat: { every: 1800000 } }); diff --git a/src/lib/queues/proxyTcpHttp.ts b/src/lib/queues/proxyTcpHttp.ts new file mode 100644 index 000000000..00b5d759e --- /dev/null +++ b/src/lib/queues/proxyTcpHttp.ts @@ -0,0 +1,34 @@ +import { ErrorHandler, generateDatabaseConfiguration, prisma } from '$lib/database'; +import { checkContainer, startTcpProxy } from '$lib/haproxy'; + +export default async function (): Promise { + try { + const databasesWithPublicPort = await prisma.database.findMany({ + where: { publicPort: { not: null } }, + include: { settings: true, destinationDocker: true } + }); + for (const database of databasesWithPublicPort) { + const { destinationDockerId, destinationDocker, publicPort, id } = database; + if (destinationDockerId) { + const { privatePort } = generateDatabaseConfiguration(database); + await startTcpProxy(destinationDocker, id, publicPort, privatePort); + } + } + const wordpressWithFtp = await prisma.wordpress.findMany({ + where: { ftpPublicPort: { not: null } }, + include: { service: { include: { destinationDocker: true } } } + }); + for (const ftp of wordpressWithFtp) { + const { service, ftpPublicPort, id } = ftp; + const { destinationDockerId, destinationDocker } = service; + if (destinationDockerId) { + await startTcpProxy(destinationDocker, `${id}-ftp`, ftpPublicPort, 22); + } + } + } catch (error) { + return ErrorHandler(error.response?.body || error); + } +} diff --git a/src/routes/databases/[id]/_Databases/_PostgreSQL.svelte b/src/routes/databases/[id]/_Databases/_PostgreSQL.svelte index ac52233f1..b5e4ba1dd 100644 --- a/src/routes/databases/[id]/_Databases/_PostgreSQL.svelte +++ b/src/routes/databases/[id]/_Databases/_PostgreSQL.svelte @@ -34,6 +34,7 @@ name="rootUserPassword" bind:value={database.rootUserPassword} /> +
diff --git a/src/routes/databases/[id]/delete.json.ts b/src/routes/databases/[id]/delete.json.ts index c81916504..d731492ad 100644 --- a/src/routes/databases/[id]/delete.json.ts +++ b/src/routes/databases/[id]/delete.json.ts @@ -1,7 +1,7 @@ import { getUserDetails } from '$lib/common'; import * as db from '$lib/database'; import { ErrorHandler, stopDatabase } from '$lib/database'; -import { deleteProxy } from '$lib/haproxy'; +import { stopTcpHttpProxy } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; export const del: RequestHandler = async (event) => { @@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => { const database = await db.getDatabase({ id, teamId }); if (database.destinationDockerId) { const everStarted = await stopDatabase(database); - if (everStarted) await deleteProxy({ id }); + if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort); } await db.removeDatabase({ id }); return { status: 200 }; diff --git a/src/routes/databases/[id]/settings.json.ts b/src/routes/databases/[id]/settings.json.ts index 042bb36ef..1f09946c6 100644 --- a/src/routes/databases/[id]/settings.json.ts +++ b/src/routes/databases/[id]/settings.json.ts @@ -1,20 +1,16 @@ import { getUserDetails } from '$lib/common'; import * as db from '$lib/database'; -import { generateDatabaseConfiguration, ErrorHandler } from '$lib/database'; +import { generateDatabaseConfiguration, ErrorHandler, getFreePort } from '$lib/database'; import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; -import getPort, { portNumbers } from 'get-port'; export const post: RequestHandler = async (event) => { const { status, body, teamId } = await getUserDetails(event); if (status === 401) return { status, body }; const { id } = event.params; - const data = await db.prisma.setting.findFirst(); - const { minPort, maxPort } = data; - const { isPublic, appendOnly = true } = await event.request.json(); - const publicPort = await getPort({ port: portNumbers(minPort, maxPort) }); + const publicPort = await getFreePort(); try { await db.setDatabase({ id, isPublic, appendOnly }); diff --git a/src/routes/services/[id]/minio/start.json.ts b/src/routes/services/[id]/minio/start.json.ts index 7c3dc9e75..4fb49f07d 100644 --- a/src/routes/services/[id]/minio/start.json.ts +++ b/src/routes/services/[id]/minio/start.json.ts @@ -4,9 +4,7 @@ import { promises as fs } from 'fs'; import yaml from 'js-yaml'; import type { RequestHandler } from '@sveltejs/kit'; import { startHttpProxy } from '$lib/haproxy'; -import getPort, { portNumbers } from 'get-port'; -import { getDomain } from '$lib/components/common'; -import { ErrorHandler, getServiceImage } from '$lib/database'; +import { ErrorHandler, getFreePort, getServiceImage } from '$lib/database'; import { makeLabelForServices } from '$lib/buildPacks/common'; import type { ComposeFile } from '$lib/types/composeFile'; @@ -28,13 +26,10 @@ export const post: RequestHandler = async (event) => { serviceSecret } = service; - const data = await db.prisma.setting.findFirst(); - const { minPort, maxPort } = data; - const network = destinationDockerId && destinationDocker.network; const host = getEngine(destinationDocker.engine); - const publicPort = await getPort({ port: portNumbers(minPort, maxPort) }); + const publicPort = await getFreePort(); const consolePort = 9001; const apiPort = 9000; diff --git a/src/routes/services/[id]/wordpress/settings.json.ts b/src/routes/services/[id]/wordpress/settings.json.ts index e82b6baba..708dc18af 100644 --- a/src/routes/services/[id]/wordpress/settings.json.ts +++ b/src/routes/services/[id]/wordpress/settings.json.ts @@ -2,7 +2,7 @@ import { dev } from '$app/env'; import { asyncExecShell, getEngine, getUserDetails } from '$lib/common'; import { decrypt, encrypt } from '$lib/crypto'; import * as db from '$lib/database'; -import { generateDatabaseConfiguration, ErrorHandler, generatePassword } from '$lib/database'; +import { ErrorHandler, generatePassword, getFreePort } from '$lib/database'; import { checkContainer, startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy'; import type { ComposeFile } from '$lib/types/composeFile'; import type { RequestHandler } from '@sveltejs/kit'; @@ -16,11 +16,10 @@ export const post: RequestHandler = async (event) => { if (status === 401) return { status, body }; const { id } = event.params; - const data = await db.prisma.setting.findFirst(); - const { minPort, maxPort } = data; const { ftpEnabled } = await event.request.json(); - const publicPort = await getPort({ port: portNumbers(minPort, maxPort) }); + const publicPort = await getFreePort(); + let ftpUser = cuid(); let ftpPassword = generatePassword();