diff --git a/package.json b/package.json index 2aa11b114..52b9ca453 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@sveltejs/adapter-node": "1.0.0-next.73", "@sveltejs/kit": "1.0.0-next.303", "@types/bcrypt": "5.0.0", + "@types/dockerode": "^3.3.8", "@types/js-cookie": "3.0.1", "@types/js-yaml": "4.0.5", "@types/node": "17.0.23", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 175ba2285..b87591e81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ specifiers: '@sveltejs/adapter-node': 1.0.0-next.73 '@sveltejs/kit': 1.0.0-next.303 '@types/bcrypt': 5.0.0 + '@types/dockerode': ^3.3.8 '@types/js-cookie': 3.0.1 '@types/js-yaml': 4.0.5 '@types/node': 17.0.23 @@ -87,6 +88,7 @@ devDependencies: '@sveltejs/adapter-node': 1.0.0-next.73 '@sveltejs/kit': 1.0.0-next.303_svelte@3.46.4 '@types/bcrypt': 5.0.0 + '@types/dockerode': 3.3.8 '@types/js-cookie': 3.0.1 '@types/js-yaml': 4.0.5 '@types/node': 17.0.23 @@ -505,6 +507,26 @@ packages: '@types/responselike': 1.0.0 dev: false + /@types/docker-modem/3.0.2: + resolution: + { + integrity: sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ== + } + dependencies: + '@types/node': 17.0.23 + '@types/ssh2': 0.5.52 + dev: true + + /@types/dockerode/3.3.8: + resolution: + { + integrity: sha512-/Hip29GzPBWfbSS87lyQDVoB7Ja+kr8oOFWXsySxNFa7jlyj3Yws8LaZRmn1xZl7uJH3Xxsg0oI09GHpT1pIBw== + } + dependencies: + '@types/docker-modem': 3.0.2 + '@types/node': 17.0.23 + dev: true + /@types/http-cache-semantics/4.0.1: resolution: { @@ -589,6 +611,25 @@ packages: '@types/node': 17.0.23 dev: true + /@types/ssh2-streams/0.1.9: + resolution: + { + integrity: sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg== + } + dependencies: + '@types/node': 17.0.23 + dev: true + + /@types/ssh2/0.5.52: + resolution: + { + integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== + } + dependencies: + '@types/node': 17.0.23 + '@types/ssh2-streams': 0.1.9 + dev: true + /@typescript-eslint/eslint-plugin/4.31.1_8ede7edd7694646e12d33c52460f622c: resolution: { diff --git a/prisma/migrations/20220408070805_added_expose_port/migration.sql b/prisma/migrations/20220408070805_added_expose_port/migration.sql new file mode 100644 index 000000000..a23afd64a --- /dev/null +++ b/prisma/migrations/20220408070805_added_expose_port/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 986a773bf..0a758bc60 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -81,6 +81,7 @@ model Application { buildPack String? projectId Int? port Int? + exposePort Int? installCommand String? buildCommand String? startCommand String? diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index 1d8140144..556188169 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -210,6 +210,7 @@ export async function configureApplication({ name, fqdn, port, + exposePort, installCommand, buildCommand, startCommand, @@ -226,6 +227,7 @@ export async function configureApplication({ buildPack, fqdn, port, + exposePort, installCommand, buildCommand, startCommand, diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index f6d57862e..0134bad20 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -38,6 +38,7 @@ export default async function (job) { build_id: buildId, configHash, port, + exposePort, installCommand, buildCommand, startCommand, @@ -143,6 +144,7 @@ export default async function (job) { JSON.stringify({ buildPack, port, + exposePort, installCommand, buildCommand, startCommand, @@ -284,6 +286,7 @@ export default async function (job) { env_file: envFound ? [`${workdir}/.env`] : [], networks: [docker.network], labels: labels, + ports: exposePort ? [`${exposePort}:${port}`] : [], depends_on: [], restart: 'always' } diff --git a/src/routes/applications/[id]/check.json.ts b/src/routes/applications/[id]/check.json.ts index dc1b8ea98..56ea05ee1 100644 --- a/src/routes/applications/[id]/check.json.ts +++ b/src/routes/applications/[id]/check.json.ts @@ -4,13 +4,14 @@ import * as db from '$lib/database'; import { ErrorHandler } from '$lib/database'; import type { RequestHandler } from '@sveltejs/kit'; import { promises as dns } from 'dns'; +import getPort from 'get-port'; export const post: RequestHandler = async (event) => { const { status, body } = await getUserDetails(event); if (status === 401) return { status, body }; const { id } = event.params; - let { fqdn, forceSave } = await event.request.json(); + let { exposePort, fqdn, forceSave } = await event.request.json(); fqdn = fqdn.toLowerCase(); try { @@ -42,6 +43,19 @@ export const post: RequestHandler = async (event) => { } } + if (exposePort) { + exposePort = Number(exposePort); + + if (exposePort < 1024 || exposePort > 65535) { + throw { message: `Expose Port needs to be between 1024 and 65535` }; + } + + const publicPort = await getPort({ port: exposePort }); + if (exposePort !== publicPort) { + throw { message: `Expose Port ${exposePort} is already in use` }; + } + } + return { status: 200 }; diff --git a/src/routes/applications/[id]/deploy.json.ts b/src/routes/applications/[id]/deploy.json.ts index 59bd7d001..e8589cf16 100644 --- a/src/routes/applications/[id]/deploy.json.ts +++ b/src/routes/applications/[id]/deploy.json.ts @@ -22,6 +22,7 @@ export const post: RequestHandler = async (event) => { JSON.stringify({ buildPack: applicationFound.buildPack, port: applicationFound.port, + exposePort: applicationFound.exposePort, installCommand: applicationFound.installCommand, buildCommand: applicationFound.buildCommand, startCommand: applicationFound.startCommand diff --git a/src/routes/applications/[id]/index.json.ts b/src/routes/applications/[id]/index.json.ts index 8a67242d2..4b9e88d79 100644 --- a/src/routes/applications/[id]/index.json.ts +++ b/src/routes/applications/[id]/index.json.ts @@ -6,6 +6,7 @@ import type { RequestHandler } from '@sveltejs/kit'; import jsonwebtoken from 'jsonwebtoken'; import { get as getRequest } from '$lib/api'; import { setDefaultConfiguration } from '$lib/buildPacks/common'; +import getPort from 'get-port'; export const get: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -49,6 +50,7 @@ export const post: RequestHandler = async (event) => { buildPack, fqdn, port, + exposePort, installCommand, buildCommand, startCommand, @@ -59,6 +61,13 @@ export const post: RequestHandler = async (event) => { pythonVariable } = await event.request.json(); if (port) port = Number(port); + if (exposePort) { + exposePort = Number(exposePort); + const publicPort = await getPort({ port: exposePort }); + if (exposePort !== publicPort) { + exposePort = -1; + } + } try { const defaultConfiguration = await setDefaultConfiguration({ @@ -76,6 +85,7 @@ export const post: RequestHandler = async (event) => { name, fqdn, port, + exposePort, installCommand, buildCommand, startCommand, diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index 4b7d6b497..c7c0ca125 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -125,7 +125,11 @@ async function handleSubmit() { loading = true; try { - await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave }); + await post(`/applications/${id}/check.json`, { + fqdn: application.fqdn, + forceSave, + exposePort: application.exposePort + }); await post(`/applications/${id}.json`, { ...application }); return window.location.reload(); } catch ({ error }) { @@ -326,7 +330,6 @@ bind:value={application.fqdn} pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$" placeholder="eg: https://coollabs.io" - required />
@@ -385,7 +388,18 @@ />
{/if} - + {#if !staticDeployments.includes(application.buildPack)} +
+ + +
+ {/if} {#if !notNodeDeployments.includes(application.buildPack)}