From 19f661706d0801dd2cc674419cb48e6716b5548e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 1 Apr 2022 15:34:29 +0200 Subject: [PATCH 01/28] fix: Ignore coolify proxy error for now --- src/lib/database/common.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/database/common.ts b/src/lib/database/common.ts index e89105f05..29d04982b 100644 --- a/src/lib/database/common.ts +++ b/src/lib/database/common.ts @@ -46,7 +46,9 @@ export function ErrorHandler(e) { if (e.message?.includes('git clone')) { truncatedError.message = 'git clone failed'; } - sentry.captureException(truncatedError); + if (!e.message?.includes('Coolify Proxy is not running')) { + sentry.captureException(truncatedError); + } const payload = { status: truncatedError.status || 500, body: { From f3b5de4697aee112e17f5b56c2e7cc4edfe30dae Mon Sep 17 00:00:00 2001 From: Restray Date: Fri, 1 Apr 2022 21:10:24 +0200 Subject: [PATCH 02/28] feat(dev): allow windows users to use pnpm dev --- package.json | 3 ++- pnpm-lock.yaml | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c7237198..f7b472958 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "2.2.7", "license": "AGPL-3.0", "scripts": { - "dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev", + "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev", "dev:stop": "docker-compose -f docker-compose-dev.yaml down", "dev:logs": "docker-compose -f docker-compose-dev.yaml logs -f --tail 10", "studio": "npx prisma studio", @@ -37,6 +37,7 @@ "@typescript-eslint/parser": "4.31.1", "@zerodevx/svelte-toast": "0.7.1", "autoprefixer": "10.4.4", + "cross-env": "^7.0.3", "cross-var": "1.1.0", "eslint": "7.32.0", "eslint-config-prettier": "8.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a89edbf25..537a1fb6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,7 @@ specifiers: compare-versions: 4.1.3 cookie: 0.4.2 cooltipz-css: ^2.1.0 + cross-env: ^7.0.3 cross-var: 1.1.0 cuid: 2.1.8 dayjs: 1.11.0 @@ -92,6 +93,7 @@ devDependencies: '@typescript-eslint/parser': 4.31.1_eslint@7.32.0+typescript@4.6.3 '@zerodevx/svelte-toast': 0.7.1 autoprefixer: 10.4.4_postcss@8.4.12 + cross-env: 7.0.3 cross-var: 1.1.0 eslint: 7.32.0 eslint-config-prettier: 8.5.0_eslint@7.32.0 @@ -2081,6 +2083,17 @@ packages: luxon: 1.28.0 dev: false + /cross-env/7.0.3: + resolution: + { + integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + } + engines: { node: '>=10.14', npm: '>=6', yarn: '>=1' } + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn/5.1.0: resolution: { integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= } dependencies: From 809f40dec938a3e9bdd0a6cf3ec8606ecba3075d Mon Sep 17 00:00:00 2001 From: Restray Date: Fri, 1 Apr 2022 21:41:22 +0200 Subject: [PATCH 03/28] feat: add loading on register button --- src/routes/register/index.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/routes/register/index.svelte b/src/routes/register/index.svelte index 35501fce2..0b2183444 100644 --- a/src/routes/register/index.svelte +++ b/src/routes/register/index.svelte @@ -19,6 +19,9 @@ emailEl.focus(); }); async function handleSubmit() { + // Prevent double submission + if (loading) return; + if (password !== passwordCheck) { return errorNotification('Passwords do not match.'); } @@ -88,8 +91,13 @@ />
- {loading ? 'Registering...' : 'Register'}
From adfc976f41555ca985e640a5c3444b59d9d24010 Mon Sep 17 00:00:00 2001 From: Mohit Yadav <48997634+Just-Moh-it@users.noreply.github.com> Date: Sat, 2 Apr 2022 17:28:42 +0530 Subject: [PATCH 04/28] Suggested change Changed to appropriate word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d02bca45..2cb2d5bf6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Live Demo https://demo.coolify.io/ -(If it is unresponsible, that means someone overloaded the server. 🙃) +(If it is unresponsive, that means someone overloaded the server. 🙃) ## How to install From b60b832426d90a7709ee7431f9b4aca8091acd95 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 15:53:25 +0200 Subject: [PATCH 05/28] chore: version++ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c7237198..01258dbcb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.2.7", + "version": "2.2.8", "license": "AGPL-3.0", "scripts": { "dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev", From ddfbda6f8064626e7466e8d415d8fd34a1b20db9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 16:22:51 +0200 Subject: [PATCH 06/28] feat: initial python support --- .../20220402135305_python/migration.sql | 4 + prisma/schema.prisma | 3 + src/lib/buildPacks/common.ts | 10 ++- src/lib/buildPacks/index.ts | 4 +- src/lib/buildPacks/python.ts | 73 +++++++++++++++++++ src/lib/components/common.ts | 2 +- src/lib/components/templates.ts | 13 ++++ src/lib/database/applications.ts | 10 ++- src/lib/queues/builder.ts | 12 ++- src/routes/applications/[id]/index.json.ts | 22 +++++- src/routes/applications/[id]/index.svelte | 56 +++++++++++++- src/tailwind.css | 4 +- 12 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 prisma/migrations/20220402135305_python/migration.sql create mode 100644 src/lib/buildPacks/python.ts diff --git a/prisma/migrations/20220402135305_python/migration.sql b/prisma/migrations/20220402135305_python/migration.sql new file mode 100644 index 000000000..0c253d539 --- /dev/null +++ b/prisma/migrations/20220402135305_python/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "Application" ADD COLUMN "pythonModule" TEXT; +ALTER TABLE "Application" ADD COLUMN "pythonVariable" TEXT; +ALTER TABLE "Application" ADD COLUMN "pythonWSGI" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a849ae36f..b7fab7185 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -87,6 +87,9 @@ model Application { baseDirectory String? publishDirectory String? phpModules String? + pythonWSGI String? + pythonModule String? + pythonVariable String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt settings ApplicationSettings? diff --git a/src/lib/buildPacks/common.ts b/src/lib/buildPacks/common.ts index 3f8b4b43e..441794217 100644 --- a/src/lib/buildPacks/common.ts +++ b/src/lib/buildPacks/common.ts @@ -100,10 +100,14 @@ export const setDefaultConfiguration = async (data) => { if (buildPack === 'static') port = 80; else if (buildPack === 'node') port = 3000; else if (buildPack === 'php') port = 80; + else if (buildPack === 'python') port = 8000; } - if (!installCommand) installCommand = template?.installCommand || 'yarn install'; - if (!startCommand) startCommand = template?.startCommand || 'yarn start'; - if (!buildCommand) buildCommand = template?.buildCommand || null; + if (template) { + if (!installCommand) installCommand = template?.installCommand || 'yarn install'; + if (!startCommand) startCommand = template?.startCommand || 'yarn start'; + if (!buildCommand) buildCommand = template?.buildCommand || null; + } + if (!publishDirectory) publishDirectory = template?.publishDirectory || null; if (baseDirectory) { if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`; diff --git a/src/lib/buildPacks/index.ts b/src/lib/buildPacks/index.ts index 41a7655f9..babbe8f17 100644 --- a/src/lib/buildPacks/index.ts +++ b/src/lib/buildPacks/index.ts @@ -12,6 +12,7 @@ import php from './php'; import rust from './rust'; import astro from './static'; import eleventy from './static'; +import python from './python'; export { node, @@ -27,5 +28,6 @@ export { php, rust, astro, - eleventy + eleventy, + python }; diff --git a/src/lib/buildPacks/python.ts b/src/lib/buildPacks/python.ts new file mode 100644 index 000000000..1aad28fe6 --- /dev/null +++ b/src/lib/buildPacks/python.ts @@ -0,0 +1,73 @@ +import { buildImage } from '$lib/docker'; +import { promises as fs } from 'fs'; + +const createDockerfile = async (data, image): Promise => { + const { + workdir, + port, + baseDirectory, + secrets, + pullmergeRequestId, + pythonWSGI, + pythonModule, + pythonVariable + } = data; + const Dockerfile: Array = []; + Dockerfile.push(`FROM ${image}`); + Dockerfile.push('WORKDIR /app'); + Dockerfile.push(`LABEL coolify.image=true`); + if (secrets.length > 0) { + secrets.forEach((secret) => { + if (secret.isBuildSecret) { + if (pullmergeRequestId) { + if (secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } else { + if (!secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } + } + }); + } + if (pythonWSGI?.toLowerCase() === 'gunicorn') { + Dockerfile.push(`RUN pip install gunicorn`); + } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { + Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`); + // Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`) + } + + try { + await fs.stat(`${workdir}${baseDirectory || ''}/requirements.txt`); + Dockerfile.push(`COPY .${baseDirectory || ''}/requirements.txt ./`); + Dockerfile.push(`RUN pip install --no-cache-dir -r .${baseDirectory || ''}/requirements.txt`); + } catch (e) { + // + } + Dockerfile.push(`COPY .${baseDirectory || ''} ./`); + Dockerfile.push(`EXPOSE ${port}`); + console.log({ pythonWSGI }); + if (pythonWSGI?.toLowerCase() === 'gunicorn') { + console.log({ pythonModule, pythonVariable }); + Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`); + } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { + Dockerfile.push( + `CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}` + ); + } else { + Dockerfile.push(`CMD python ${pythonModule}`); + } + + await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); +}; + +export default async function (data) { + try { + const image = 'python:3-alpine'; + await createDockerfile(data, image); + await buildImage(data); + } catch (error) { + throw error; + } +} diff --git a/src/lib/components/common.ts b/src/lib/components/common.ts index b9b3ac982..96ae8fb52 100644 --- a/src/lib/components/common.ts +++ b/src/lib/components/common.ts @@ -19,7 +19,7 @@ export const staticDeployments = [ 'astro', 'eleventy' ]; -export const notNodeDeployments = ['php', 'docker', 'rust']; +export const notNodeDeployments = ['php', 'docker', 'rust', 'python']; export function getDomain(domain) { return domain?.replace('https://', '').replace('http://', ''); diff --git a/src/lib/components/templates.ts b/src/lib/components/templates.ts index 5ad62f6fa..b82b12f51 100644 --- a/src/lib/components/templates.ts +++ b/src/lib/components/templates.ts @@ -146,6 +146,13 @@ export function findBuildPack(pack, packageManager = 'npm') { port: 80 }; } + if (pack === 'python') { + return { + ...metaData, + startCommand: null, + port: 8000 + }; + } return { name: 'node', fancyName: 'Node.js', @@ -249,6 +256,12 @@ export const buildPacks = [ fancyName: 'Rust', hoverColor: 'hover:bg-pink-700', color: 'bg-pink-700' + }, + { + name: 'python', + fancyName: 'Python', + hoverColor: 'hover:bg-green-700', + color: 'bg-green-700' } ]; export const scanningTemplates = { diff --git a/src/lib/database/applications.ts b/src/lib/database/applications.ts index ad03ed7c0..1d8140144 100644 --- a/src/lib/database/applications.ts +++ b/src/lib/database/applications.ts @@ -214,11 +214,15 @@ export async function configureApplication({ buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + pythonWSGI, + pythonModule, + pythonVariable }) { return await prisma.application.update({ where: { id }, data: { + name, buildPack, fqdn, port, @@ -227,7 +231,9 @@ export async function configureApplication({ startCommand, baseDirectory, publishDirectory, - name + pythonWSGI, + pythonModule, + pythonVariable } }); } diff --git a/src/lib/queues/builder.ts b/src/lib/queues/builder.ts index 3898bcdb5..be46976c5 100644 --- a/src/lib/queues/builder.ts +++ b/src/lib/queues/builder.ts @@ -51,7 +51,10 @@ export default async function (job) { pullmergeRequestId = null, sourceBranch = null, settings, - persistentStorage + persistentStorage, + pythonWSGI, + pythonModule, + pythonVariable } = job.data; const { debug } = settings; @@ -127,7 +130,7 @@ export default async function (job) { } try { - db.prisma.build.update({ where: { id: buildId }, data: { commit } }); + await db.prisma.build.update({ where: { id: buildId }, data: { commit } }); } catch (err) { console.log(err); } @@ -200,7 +203,10 @@ export default async function (job) { startCommand, baseDirectory, secrets, - phpModules + phpModules, + pythonWSGI, + pythonModule, + pythonVariable }); else { saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); diff --git a/src/routes/applications/[id]/index.json.ts b/src/routes/applications/[id]/index.json.ts index 3b4405706..8a67242d2 100644 --- a/src/routes/applications/[id]/index.json.ts +++ b/src/routes/applications/[id]/index.json.ts @@ -5,6 +5,7 @@ import { checkContainer } from '$lib/haproxy'; import type { RequestHandler } from '@sveltejs/kit'; import jsonwebtoken from 'jsonwebtoken'; import { get as getRequest } from '$lib/api'; +import { setDefaultConfiguration } from '$lib/buildPacks/common'; export const get: RequestHandler = async (event) => { const { teamId, status, body } = await getUserDetails(event); @@ -52,12 +53,23 @@ export const post: RequestHandler = async (event) => { buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + pythonWSGI, + pythonModule, + pythonVariable } = await event.request.json(); - if (port) port = Number(port); try { + const defaultConfiguration = await setDefaultConfiguration({ + buildPack, + port, + installCommand, + startCommand, + buildCommand, + publishDirectory, + baseDirectory + }); await db.configureApplication({ id, buildPack, @@ -68,7 +80,11 @@ export const post: RequestHandler = async (event) => { buildCommand, startCommand, baseDirectory, - publishDirectory + publishDirectory, + pythonWSGI, + pythonModule, + pythonVariable, + ...defaultConfiguration }); return { status: 201 }; } catch (error) { diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index 49f800975..bcdbfc876 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -38,6 +38,7 @@ import { page, session } from '$app/stores'; import { errorNotification } from '$lib/form'; import { onMount } from 'svelte'; + import Select from 'svelte-select'; import Explainer from '$lib/components/Explainer.svelte'; import Setting from '$lib/components/Setting.svelte'; @@ -57,6 +58,23 @@ let previews = application.settings.previews; let dualCerts = application.settings.dualCerts; let autodeploy = application.settings.autodeploy; + + let wsgis = [ + { + value: 'None', + label: 'None' + }, + { + value: 'Gunicorn', + label: 'Gunicorn' + } + // }, + // { + // value: 'uWSGI', + // label: 'uWSGI' + // } + ]; + if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) { application.fqdn = `http://${cuid()}.demo.coolify.io`; } @@ -119,6 +137,9 @@ loading = false; } } + async function selectWSGI(event) { + application.pythonWSGI = event.detail.value; + }
@@ -315,6 +336,39 @@ on:click={() => !isRunning && changeSettings('dualCerts')} />
+ {#if application.buildPack === 'python'} +
+ +
+ +
+
+ + +
+ {/if} {#if !staticDeployments.includes(application.buildPack)}
@@ -323,7 +377,7 @@ name="port" id="port" bind:value={application.port} - placeholder="default: 3000" + placeholder={application.buildPack === 'python' ? '8000' : '3000'} />
{/if} diff --git a/src/tailwind.css b/src/tailwind.css index 1f357acfb..20f67dd73 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -54,10 +54,10 @@ #svelte .listContainer { } #svelte .item.hover { - @apply bg-coolgray-100 text-white; + @apply bg-coollabs text-white !important; } #svelte .item.active { - @apply bg-coollabs text-white; + @apply bg-coolgray-100 text-white; } select { From 78293340cc87371fcc67bf5d8abdc0fbf023121d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 16:27:44 +0200 Subject: [PATCH 07/28] chore: version++ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01258dbcb..bfb67698f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "coolify", "description": "An open-source & self-hostable Heroku / Netlify alternative.", - "version": "2.2.8", + "version": "2.3.0", "license": "AGPL-3.0", "scripts": { "dev": "docker-compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev", From 066f5b25e0e347403e3bc917a174a18d6fa4e82d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 17:05:32 +0200 Subject: [PATCH 08/28] fix: Python no wsgi --- src/routes/applications/[id]/index.svelte | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index bcdbfc876..8b94d4795 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -352,22 +352,22 @@ id="pythonModule" required bind:value={application.pythonModule} - placeholder={application.pythonWSGI?.toLowerCase() !== 'gunicorn' - ? 'myapp.py' - : 'myapp'} - /> -
-
- -
+ {#if application.pythonWSGI?.toLowerCase() === 'gunicorn'} +
+ + +
+ {/if} {/if} {#if !staticDeployments.includes(application.buildPack)}
From 7a74ba17965f9bca7f24925736861cb43deca5d9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Sat, 2 Apr 2022 17:43:24 +0200 Subject: [PATCH 09/28] ui: Improvements --- src/lib/components/DatabaseLinks.svelte | 25 +++++ src/lib/components/ServiceLinks.svelte | 55 +++++++++++ src/routes/applications/[id]/__layout.svelte | 4 +- src/routes/applications/[id]/index.svelte | 14 ++- .../applications/[id]/logs/build/index.svelte | 71 +++++++++++++- .../applications/[id]/logs/index.svelte | 73 +++++++++++++- .../applications/[id]/previews/index.svelte | 97 ++++++++++++++++--- .../applications/[id]/secrets/index.svelte | 71 +++++++++++++- .../applications/[id]/storage/index.svelte | 75 +++++++++++++- src/routes/databases/[id]/index.svelte | 31 ++---- .../services/[id]/_Services/_Services.svelte | 4 +- src/routes/services/[id]/index.svelte | 66 ++----------- src/routes/services/[id]/secrets/index.svelte | 38 +++++++- src/routes/settings/index.svelte | 2 +- 14 files changed, 501 insertions(+), 125 deletions(-) create mode 100644 src/lib/components/DatabaseLinks.svelte create mode 100644 src/lib/components/ServiceLinks.svelte diff --git a/src/lib/components/DatabaseLinks.svelte b/src/lib/components/DatabaseLinks.svelte new file mode 100644 index 000000000..9ef11a238 --- /dev/null +++ b/src/lib/components/DatabaseLinks.svelte @@ -0,0 +1,25 @@ + + + + {#if database.type === 'clickhouse'} + + {:else if database.type === 'couchdb'} + + {:else if database.type === 'mongodb'} + + {:else if database.type === 'mysql'} + + {:else if database.type === 'postgresql'} + + {:else if database.type === 'redis'} + + {/if} + diff --git a/src/lib/components/ServiceLinks.svelte b/src/lib/components/ServiceLinks.svelte new file mode 100644 index 000000000..a3b4ce2cd --- /dev/null +++ b/src/lib/components/ServiceLinks.svelte @@ -0,0 +1,55 @@ + + +{#if service.type === 'plausibleanalytics'} + + + +{:else if service.type === 'nocodb'} + + + +{:else if service.type === 'minio'} + + + +{:else if service.type === 'vscodeserver'} + + + +{:else if service.type === 'wordpress'} + + + +{:else if service.type === 'vaultwarden'} + + + +{:else if service.type === 'languagetool'} + + + +{:else if service.type === 'n8n'} + + + +{:else if service.type === 'uptimekuma'} + + + +{:else if service.type === 'ghost'} + + + +{/if} diff --git a/src/routes/applications/[id]/__layout.svelte b/src/routes/applications/[id]/__layout.svelte index 4a79bba44..7c6f528b7 100644 --- a/src/routes/applications/[id]/__layout.svelte +++ b/src/routes/applications/[id]/__layout.svelte @@ -255,9 +255,9 @@ class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`} >