From 9691010e7ba17db7af0681ba6aa879fc20df62b1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 11:11:55 +0100 Subject: [PATCH 1/7] test --- .github/workflows/staging-release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index c4a9988c4..72d6c9444 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -87,6 +87,8 @@ jobs: run: | docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest push coollabsio/coolify:next + docker manifest create coollabsio/coolify:test --amend coollabsio/coolify:next + docker manifest push coollabsio/coolify:test - uses: sarisia/actions-status-discord@v1 if: always() with: From 5676bd9d0d4432d1dd90f291814c08f9038883ce Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 11:24:19 +0100 Subject: [PATCH 2/7] test --- .github/workflows/staging-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index 72d6c9444..457cc0186 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -87,8 +87,8 @@ jobs: run: | docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest push coollabsio/coolify:next - docker manifest create coollabsio/coolify:test --amend coollabsio/coolify:next - docker manifest push coollabsio/coolify:test + docker tag coollabsio/coolify:next coollabsio/coolify:test + docker push coollabsio/coolify:test - uses: sarisia/actions-status-discord@v1 if: always() with: From 58a42abc674aec77990c715f7e35b39dcf2d4b6b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 11:40:47 +0100 Subject: [PATCH 3/7] test --- .github/workflows/staging-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index 457cc0186..b7f98f6e6 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -87,6 +87,7 @@ jobs: run: | docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest push coollabsio/coolify:next + docker pull coollabsio/coolify:next docker tag coollabsio/coolify:next coollabsio/coolify:test docker push coollabsio/coolify:test - uses: sarisia/actions-status-discord@v1 From e566a66ea4563a7e340ffb94d9b7fe482c76c4eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 12:33:27 +0100 Subject: [PATCH 4/7] test --- .github/workflows/staging-release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index b7f98f6e6..c65feb565 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -87,9 +87,8 @@ jobs: run: | docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest push coollabsio/coolify:next - docker pull coollabsio/coolify:next - docker tag coollabsio/coolify:next coollabsio/coolify:test - docker push coollabsio/coolify:test + docker manifest create coollabsio/coolify:test --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 + docker manifest push coollabsio/coolify:test - uses: sarisia/actions-status-discord@v1 if: always() with: From 4fa0f2d04a34254f3bcc4a5d68cd43b83d4abf18 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 12:47:42 +0100 Subject: [PATCH 5/7] fix: gh actions --- .github/workflows/production-release.yml | 2 ++ .github/workflows/staging-release.yml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml index 44b5f7003..7b98d5cf9 100644 --- a/.github/workflows/production-release.yml +++ b/.github/workflows/production-release.yml @@ -104,7 +104,9 @@ jobs: - name: Create & publish manifest run: | docker manifest create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 + docker manifest create coollabsio/coolify:latest --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-amd64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --amend coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 docker manifest push coollabsio/coolify:${{steps.package-version.outputs.current-version}} + docker manifest push coollabsio/coolify:latest - uses: sarisia/actions-status-discord@v1 if: always() with: diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml index c65feb565..c4a9988c4 100644 --- a/.github/workflows/staging-release.yml +++ b/.github/workflows/staging-release.yml @@ -87,8 +87,6 @@ jobs: run: | docker manifest create coollabsio/coolify:next --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 docker manifest push coollabsio/coolify:next - docker manifest create coollabsio/coolify:test --amend coollabsio/coolify:next-amd64 --amend coollabsio/coolify:next-arm64 - docker manifest push coollabsio/coolify:test - uses: sarisia/actions-status-discord@v1 if: always() with: From 9c6f412f04be78c5ec2adec9c0336332011c6e59 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 21 Dec 2022 13:06:44 +0100 Subject: [PATCH 6/7] wip: trpc --- apps/api/src/lib/common.ts | 1 + apps/client/package.json | 5 +- apps/client/src/app.d.ts | 3 + apps/client/src/lib/common.ts | 167 ++- apps/client/src/lib/components/Beta.svelte | 1 + .../lib/components/CopyPasswordField.svelte | 156 +++ .../src/lib/components/Explainer.svelte | 38 + apps/client/src/lib/components/Setting.svelte | 87 ++ apps/client/src/lib/store.ts | 33 +- apps/client/src/routes/+page.ts | 5 +- .../src/routes/applications/[id]/+page.svelte | 1228 +++++++++++++++++ .../applications/[id]/features/+page.svelte | 118 ++ .../applications/[id]/secrets/+page.svelte | 138 ++ .../routes/applications/[id]/secrets/+page.ts | 16 + .../secrets/_components/PreviewSecret.svelte | 131 ++ .../[id]/secrets/_components/Secret.svelte | 193 +++ .../applications/[id]/storages/+page.svelte | 78 ++ .../applications/[id]/storages/+page.ts | 16 + .../[id]/storages/components/Storage.svelte | 114 ++ .../src/routes/applications/[id]/utils.ts | 58 +- apps/client/vite.config.ts | 8 +- apps/server/package.json | 1 + apps/server/src/lib/common.ts | 345 +++++ apps/server/src/trpc/context.ts | 2 +- .../src/trpc/routers/applications/index.ts | 472 ++++++- pnpm-lock.yaml | 8 + 26 files changed, 3383 insertions(+), 39 deletions(-) create mode 100644 apps/client/src/lib/components/Beta.svelte create mode 100644 apps/client/src/lib/components/CopyPasswordField.svelte create mode 100644 apps/client/src/lib/components/Explainer.svelte create mode 100644 apps/client/src/lib/components/Setting.svelte create mode 100644 apps/client/src/routes/applications/[id]/features/+page.svelte create mode 100644 apps/client/src/routes/applications/[id]/secrets/+page.svelte create mode 100644 apps/client/src/routes/applications/[id]/secrets/+page.ts create mode 100644 apps/client/src/routes/applications/[id]/secrets/_components/PreviewSecret.svelte create mode 100644 apps/client/src/routes/applications/[id]/secrets/_components/Secret.svelte create mode 100644 apps/client/src/routes/applications/[id]/storages/+page.svelte create mode 100644 apps/client/src/routes/applications/[id]/storages/+page.ts create mode 100644 apps/client/src/routes/applications/[id]/storages/components/Storage.svelte diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts index 69a77aa9a..bef297488 100644 --- a/apps/api/src/lib/common.ts +++ b/apps/api/src/lib/common.ts @@ -1920,3 +1920,4 @@ export function generateSecrets( } return envs; } + diff --git a/apps/client/package.json b/apps/client/package.json index b9fda399a..220c9b248 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -44,7 +44,10 @@ "daisyui": "2.41.0", "flowbite-svelte": "0.28.0", "js-cookie": "3.0.1", + "js-yaml": "4.1.0", + "p-limit": "4.0.0", "server": "workspace:*", - "superjson": "1.11.0" + "superjson": "1.11.0", + "svelte-select": "4.4.7" } } diff --git a/apps/client/src/app.d.ts b/apps/client/src/app.d.ts index 8f4d63895..b527fe7bd 100644 --- a/apps/client/src/app.d.ts +++ b/apps/client/src/app.d.ts @@ -7,3 +7,6 @@ declare namespace App { // interface Error {} // interface Platform {} } + +declare const GITPOD_WORKSPACE_URL: string; +declare const CODESANDBOX_HOST: string; diff --git a/apps/client/src/lib/common.ts b/apps/client/src/lib/common.ts index ab3b55370..168c36ed1 100644 --- a/apps/client/src/lib/common.ts +++ b/apps/client/src/lib/common.ts @@ -1,14 +1,17 @@ +import { dev } from '$app/environment'; import { addToast } from './store'; - +import Cookies from 'js-cookie'; export const asyncSleep = (delay: number) => new Promise((resolve) => setTimeout(resolve, delay)); export function errorNotification(error: any | { message: string }): void { if (error instanceof Error) { + console.error(error.message) addToast({ message: error.message, type: 'error' }); } else { + console.error(error) addToast({ message: error, type: 'error' @@ -18,3 +21,165 @@ export function errorNotification(error: any | { message: string }): void { export function getRndInteger(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } + +export function getDomain(domain: string) { + return domain?.replace('https://', '').replace('http://', ''); +} + +export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel', 'heroku']; +export const staticDeployments = [ + 'react', + 'vuejs', + 'static', + 'svelte', + 'gatsby', + 'php', + 'astro', + 'eleventy' +]; + +export function getAPIUrl() { + if (GITPOD_WORKSPACE_URL) { + const { href } = new URL(GITPOD_WORKSPACE_URL); + const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, ''); + return newURL; + } + if (CODESANDBOX_HOST) { + return `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`; + } + return dev ? `http://${window.location.hostname}:3001` : 'http://localhost:3000'; +} +export function getWebhookUrl(type: string) { + if (GITPOD_WORKSPACE_URL) { + const { href } = new URL(GITPOD_WORKSPACE_URL); + const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, ''); + if (type === 'github') { + return `${newURL}/webhooks/github/events`; + } + if (type === 'gitlab') { + return `${newURL}/webhooks/gitlab/events`; + } + } + if (CODESANDBOX_HOST) { + const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`; + if (type === 'github') { + return `${newURL}/webhooks/github/events`; + } + if (type === 'gitlab') { + return `${newURL}/webhooks/gitlab/events`; + } + } + return `https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21/events`; +} + +async function send({ + method, + path, + data = null, + headers, + timeout = 120000 +}: { + method: string; + path: string; + data?: any; + headers?: any; + timeout?: number; +}): Promise> { + const token = Cookies.get('token'); + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const opts: any = { method, headers: {}, body: null, signal: controller.signal }; + if (data && Object.keys(data).length > 0) { + const parsedData = data; + for (const [key, value] of Object.entries(data)) { + if (value === '') { + parsedData[key] = null; + } + } + if (parsedData) { + opts.headers['Content-Type'] = 'application/json'; + opts.body = JSON.stringify(parsedData); + } + } + + if (headers) { + opts.headers = { + ...opts.headers, + ...headers + }; + } + if (token && !path.startsWith('https://')) { + opts.headers = { + ...opts.headers, + Authorization: `Bearer ${token}` + }; + } + if (!path.startsWith('https://')) { + path = `/api/v1${path}`; + } + + if (dev && !path.startsWith('https://')) { + path = `${getAPIUrl()}${path}`; + } + if (method === 'POST' && data && !opts.body) { + opts.body = data; + } + const response = await fetch(`${path}`, opts); + + clearTimeout(id); + + const contentType = response.headers.get('content-type'); + + let responseData = {}; + if (contentType) { + if (contentType?.indexOf('application/json') !== -1) { + responseData = await response.json(); + } else if (contentType?.indexOf('text/plain') !== -1) { + responseData = await response.text(); + } else { + return {}; + } + } else { + return {}; + } + if (!response.ok) { + if ( + response.status === 401 && + !path.startsWith('https://api.github') && + !path.includes('/v4/') + ) { + Cookies.remove('token'); + } + + throw responseData; + } + return responseData; +} + +export function get(path: string, headers?: Record): Promise> { + return send({ method: 'GET', path, headers }); +} + +export function del( + path: string, + data: Record, + headers?: Record +): Promise> { + return send({ method: 'DELETE', path, data, headers }); +} + +export function post( + path: string, + data: Record | FormData, + headers?: Record +): Promise> { + return send({ method: 'POST', path, data, headers }); +} + +export function put( + path: string, + data: Record, + headers?: Record +): Promise> { + return send({ method: 'PUT', path, data, headers }); +} diff --git a/apps/client/src/lib/components/Beta.svelte b/apps/client/src/lib/components/Beta.svelte new file mode 100644 index 000000000..279401fcf --- /dev/null +++ b/apps/client/src/lib/components/Beta.svelte @@ -0,0 +1 @@ + BETA \ No newline at end of file diff --git a/apps/client/src/lib/components/CopyPasswordField.svelte b/apps/client/src/lib/components/CopyPasswordField.svelte new file mode 100644 index 000000000..a0a474750 --- /dev/null +++ b/apps/client/src/lib/components/CopyPasswordField.svelte @@ -0,0 +1,156 @@ + + +
+ {#if !isPasswordField || showPassword} + {#if textarea} + + {:else} + + {/if} + {:else} + + {/if} + +
+
+ {#if isPasswordField} + +
(showPassword = !showPassword)}> + {#if showPassword} + + + + {:else} + + + + + {/if} +
+ {/if} + {#if value && isHttps} + +
+ + + + + +
+ {/if} +
+
+
diff --git a/apps/client/src/lib/components/Explainer.svelte b/apps/client/src/lib/components/Explainer.svelte new file mode 100644 index 000000000..924ce70d6 --- /dev/null +++ b/apps/client/src/lib/components/Explainer.svelte @@ -0,0 +1,38 @@ + + +
+ + + + + +
diff --git a/apps/client/src/lib/components/Setting.svelte b/apps/client/src/lib/components/Setting.svelte new file mode 100644 index 000000000..555323b37 --- /dev/null +++ b/apps/client/src/lib/components/Setting.svelte @@ -0,0 +1,87 @@ + + +
+
+ + +
+
+
+ +
+ Use setting + + + + +
+
+ +{#if dataTooltip} + {dataTooltip} +{/if} diff --git a/apps/client/src/lib/store.ts b/apps/client/src/lib/store.ts index 8e3687dca..77510ba95 100644 --- a/apps/client/src/lib/store.ts +++ b/apps/client/src/lib/store.ts @@ -21,7 +21,8 @@ export const trpc = createTRPCProxyClient({ }) ] }); - +export const disabledButton: Writable = writable(false); +export const location: Writable = writable(null) interface AppSession { isRegistrationEnabled: boolean; token?: string; @@ -139,3 +140,33 @@ export const status: Writable = writable({ isPublic: false } }); + +export function checkIfDeploymentEnabledApplications(isAdmin: boolean, application: any) { + return !!( + (isAdmin && application.buildPack === 'compose') || + ((application.fqdn || application.settings.isBot) && + ((application.gitSource && application.repository && application.buildPack) || + application.simpleDockerfile) && + application.destinationDocker) + ); +} +export const setLocation = (resource: any, settings?: any) => { + if (resource.settings.isBot && resource.exposePort) { + disabledButton.set(false); + return location.set(`http://${dev ? 'localhost' : settings.ipv4}:${resource.exposePort}`); + } + if (GITPOD_WORKSPACE_URL && resource.exposePort) { + const { href } = new URL(GITPOD_WORKSPACE_URL); + const newURL = href.replace('https://', `https://${resource.exposePort}-`).replace(/\/$/, ''); + return location.set(newURL); + } else if (CODESANDBOX_HOST) { + const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, resource.exposePort)}`; + return location.set(newURL); + } + if (resource.fqdn) { + return location.set(resource.fqdn); + } else { + location.set(null); + disabledButton.set(false); + } +}; diff --git a/apps/client/src/routes/+page.ts b/apps/client/src/routes/+page.ts index 4652e3d53..3465b727e 100644 --- a/apps/client/src/routes/+page.ts +++ b/apps/client/src/routes/+page.ts @@ -1,11 +1,8 @@ import { error } from '@sveltejs/kit'; import { trpc } from '$lib/store'; -import type { LayoutLoad } from './$types'; -import { redirect } from '@sveltejs/kit'; -import Cookies from 'js-cookie'; export const ssr = false; -export const load: LayoutLoad = async ({ url }) => { +export const load = async () => { try { return await trpc.dashboard.resources.query(); } catch (err) { diff --git a/apps/client/src/routes/applications/[id]/+page.svelte b/apps/client/src/routes/applications/[id]/+page.svelte index e69de29bb..0a431b054 100644 --- a/apps/client/src/routes/applications/[id]/+page.svelte +++ b/apps/client/src/routes/applications/[id]/+page.svelte @@ -0,0 +1,1228 @@ + + +
+
handleSubmit()}> +
+
+
General
+ {#if $appSession.isAdmin} + + {/if} +
+
+
+ + +
+ {#if !isSimpleDockerfile} +
+ + {#if isDisabled || application.settings?.isPublicRepository} + + {:else} + + {/if} +
+
+ + +
+
+ + {#if isDisabled || application.settings?.isPublicRepository} + + {:else} + + {/if} +
+ {/if} +
+ + {#if isDisabled} + + {:else} + + + {/if} +
+ {#if application.dockerRegistry?.id && application.gitSourceId} +
+ + +
+ {/if} + {#if !isSimpleDockerfile} +
+ + {#if isDisabled} + + {:else} + + + {/if} +
+ {/if} +
+ +
+ +
+
+ {#if application.buildPack !== 'compose'} +
+ changeSettings('isBot')} + title="Is your application a bot?" + description="You can deploy applications without domains or make them to listen on the Exposed Port.

Useful to host Twitch bots, regular jobs, or anything that does not require an incoming HTTP connection." + disabled={isDisabled} + /> +
+ {/if} + {#if !isBot && application.buildPack !== 'compose'} +
+ +
+ + {#if forceSave} +
+ {#if isNonWWWDomainOK} + + {:else} + + {/if} + {#if dualCerts} + {#if isWWWDomainOK} + + {:else} + + {/if} + {/if} +
+ {/if} +
+
+
+ !isDisabled && changeSettings('dualCerts')} + /> +
+ {#if isHttps && application.buildPack !== 'compose'} +
+ changeSettings('isCustomSSL')} + /> +
+ {/if} + {/if} +
+ {#if isSimpleDockerfile} +
+ Configuration +
+ +
+
+ +
+