Compare commits

...

22 Commits

Author SHA1 Message Date
Andras Bacsai
1881e646d4 Merge pull request #132 from coollabsio/next
v2.0.11
2022-02-15 10:51:25 +01:00
Andras Bacsai
aa98808a1a fix: Typo 2022-02-15 10:46:44 +01:00
Andras Bacsai
f9a2232703 fix: Small fixes 2022-02-15 10:42:26 +01:00
Andras Bacsai
19d6be8663 fix: Load more button 2022-02-15 10:35:22 +01:00
Andras Bacsai
0eb7c890ad fix: GitHub sync PR's 2022-02-15 10:25:23 +01:00
Andras Bacsai
7bfa68aa58 chore: version++ 2022-02-15 09:40:04 +01:00
Andras Bacsai
857a38050e fix: Window error in SSR 2022-02-15 09:39:45 +01:00
Andras Bacsai
c5b7f92caf feat: Follow logs 2022-02-15 09:38:16 +01:00
Andras Bacsai
df31ffd7fb Merge pull request #127 from SaraVieira/logs-improvements
UX improvements for the log page
2022-02-15 09:02:43 +01:00
Andras Bacsai
0df0322d36 Merge pull request #129 from coollabsio/next
v2.0.10
2022-02-15 08:52:45 +01:00
Andras Bacsai
260552322d chore: Version++ 2022-02-15 08:50:17 +01:00
Andras Bacsai
88ef6496a2 fix: Coolify proxy start 2022-02-15 08:50:02 +01:00
Andras Bacsai
bdf123bf7b fix: Stopping service without proxy 2022-02-15 08:38:16 +01:00
Andras Bacsai
8fc3760eef fix: Error handling 2022-02-14 16:52:00 +01:00
Sara Vieira
5656f6f709 add env example back 2022-02-14 16:01:08 +01:00
Andras Bacsai
53e7e8b77e version bump 2022-02-14 15:59:00 +01:00
Andras Bacsai
b990915b7a fix: Typo 2022-02-14 15:58:44 +01:00
Sara Vieira
15b7822ffd put db in the right place 2022-02-14 15:58:42 +01:00
Sara Vieira
cfa28419cb add yarn lock to gitignore 2022-02-14 15:56:28 +01:00
Sara Vieira
30ef0d2a3a some log improvements 2022-02-14 15:55:19 +01:00
Andras Bacsai
755f99200a Create README.md 2022-02-14 11:32:19 +01:00
Andras Bacsai
7af79ed3a2 Update README.md 2022-02-14 11:29:56 +01:00
27 changed files with 268 additions and 150 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@ node_modules
/build /build
/.svelte-kit /.svelte-kit
/package /package
/yarn.lock
.env .env
.env.prod .env.prod

View File

@@ -71,9 +71,7 @@ You can use the official ones or your self hosted version!
## Roadmap ## Roadmap
[See the Roadmap here](https://github.com/coollabsio/coolify/projects/1) [See the Roadmap here](https://github.com/orgs/coollabsio/projects/3/views/8)
(Will be updated soon!)
## License ## License

View File

@@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "2.0.8", "version": "2.0.11",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0", "dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",

View File

@@ -59,10 +59,14 @@ export async function removeApplication({ id, teamId }) {
const id = containerObj.ID; const id = containerObj.ID;
const preview = containerObj.Image.split('-')[1]; const preview = containerObj.Image.split('-')[1];
await removeDestinationDocker({ id, engine: destinationDocker.engine }); await removeDestinationDocker({ id, engine: destinationDocker.engine });
if (preview) { try {
await removeProxyConfiguration({ domain: `${preview}.${domain}` }); if (preview) {
} else { await removeProxyConfiguration({ domain: `${preview}.${domain}` });
await removeProxyConfiguration({ domain }); } else {
await removeProxyConfiguration({ domain });
}
} catch (error) {
console.log(error);
} }
} }
} }

View File

@@ -41,7 +41,7 @@ export function ErrorHandler(e) {
e = new Error(e.toString()); e = new Error(e.toString());
} }
let truncatedError = e; let truncatedError = e;
if (e.message.includes('docker run')) { if (e.message?.includes('docker run')) {
let truncatedArray = []; let truncatedArray = [];
truncatedArray = truncatedError.message.split('-').filter((line) => { truncatedArray = truncatedError.message.split('-').filter((line) => {
if (!line.startsWith('e ')) { if (!line.startsWith('e ')) {
@@ -50,7 +50,7 @@ export function ErrorHandler(e) {
}); });
truncatedError.message = truncatedArray.join('-'); truncatedError.message = truncatedArray.join('-');
} }
if (e.message.includes('git clone')) { if (e.message?.includes('git clone')) {
truncatedError.message = 'git clone failed'; truncatedError.message = 'git clone failed';
} }
sentry.captureException(truncatedError); sentry.captureException(truncatedError);
@@ -61,11 +61,11 @@ export function ErrorHandler(e) {
error: truncatedError.error || truncatedError.message error: truncatedError.error || truncatedError.message
} }
}; };
if (truncatedError.name === 'NotFoundError') { if (truncatedError?.name === 'NotFoundError') {
payload.status = 404; payload.status = 404;
} }
if (truncatedError instanceof P.PrismaClientKnownRequestError) { if (truncatedError instanceof P.PrismaClientKnownRequestError) {
if (truncatedError.code === 'P2002') { if (truncatedError?.code === 'P2002') {
payload.body.message = 'Already exists. Choose another name.'; payload.body.message = 'Already exists. Choose another name.';
} }
} }

View File

@@ -107,11 +107,7 @@ export async function forceSSLOffApplication({ domain }) {
export async function forceSSLOnApplication({ domain }) { export async function forceSSLOnApplication({ domain }) {
if (!dev) { if (!dev) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
const transactionId = await getNextTransactionId(); const transactionId = await getNextTransactionId();
try { try {
@@ -162,11 +158,7 @@ export async function forceSSLOnApplication({ domain }) {
export async function deleteProxy({ id }) { export async function deleteProxy({ id }) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
const transactionId = await getNextTransactionId(); const transactionId = await getNextTransactionId();
try { try {
await haproxy.get(`v2/services/haproxy/configuration/backends/${id}`).json(); await haproxy.get(`v2/services/haproxy/configuration/backends/${id}`).json();
@@ -198,11 +190,7 @@ export async function reloadHaproxy(engine) {
} }
export async function configureProxyForApplication({ domain, imageId, applicationId, port }) { export async function configureProxyForApplication({ domain, imageId, applicationId, port }) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
let serverConfigured = false; let serverConfigured = false;
let backendAvailable: any = null; let backendAvailable: any = null;
@@ -283,11 +271,7 @@ export async function configureProxyForApplication({ domain, imageId, applicatio
export async function configureCoolifyProxyOff(fqdn) { export async function configureCoolifyProxyOff(fqdn) {
const domain = getDomain(fqdn); const domain = getDomain(fqdn);
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
try { try {
const transactionId = await getNextTransactionId(); const transactionId = await getNextTransactionId();
@@ -308,22 +292,21 @@ export async function configureCoolifyProxyOff(fqdn) {
throw error?.response?.body || error; throw error?.response?.body || error;
} }
} }
export async function checkHAProxy(haproxy) { export async function checkHAProxy(haproxy?: any) {
if (!haproxy) haproxy = await haproxyInstance(); if (!haproxy) haproxy = await haproxyInstance();
try { try {
await haproxy.get('v2/info'); await haproxy.get('v2/info');
} catch (error) { } catch (error) {
throw 'HAProxy is not running, but it should be!'; throw {
message:
'Coolify Proxy is not running, but it should be!<br><br>Start it in the "Destinations" menu.'
};
} }
} }
export async function configureCoolifyProxyOn(fqdn) { export async function configureCoolifyProxyOn(fqdn) {
const domain = getDomain(fqdn); const domain = getDomain(fqdn);
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
let serverConfigured = false; let serverConfigured = false;
let backendAvailable: any = null; let backendAvailable: any = null;
try { try {
@@ -460,7 +443,7 @@ export async function startCoolifyProxy(engine) {
); );
const ip = JSON.parse(Config)[0].Gateway; const ip = JSON.parse(Config)[0].Gateway;
await asyncExecShell( await asyncExecShell(
`DOCKER_HOST="${host}" docker run -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restarts always --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' -v coolify-ssl-certs:/usr/local/etc/haproxy/ssl --network coolify-infra -p "80:80" -p "443:443" -p "8404:8404" -p "5555:5555" -p "5000:5000" --name coolify-haproxy -d coollabsio/${defaultProxyImage}` `DOCKER_HOST="${host}" docker run -e HAPROXY_USERNAME=${proxyUser} -e HAPROXY_PASSWORD=${proxyPassword} --restart always --add-host 'host.docker.internal:host-gateway' --add-host 'host.docker.internal:${ip}' -v coolify-ssl-certs:/usr/local/etc/haproxy/ssl --network coolify-infra -p "80:80" -p "443:443" -p "8404:8404" -p "5555:5555" -p "5000:5000" --name coolify-haproxy -d coollabsio/${defaultProxyImage}`
); );
} }
await configureNetworkCoolifyProxy(engine); await configureNetworkCoolifyProxy(engine);
@@ -572,12 +555,7 @@ export async function configureSimpleServiceProxyOn({ id, domain, port }) {
export async function configureSimpleServiceProxyOff({ domain }) { export async function configureSimpleServiceProxyOff({ domain }) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
try { try {
await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json(); await haproxy.get(`v2/services/haproxy/configuration/backends/${domain}`).json();
const transactionId = await getNextTransactionId(); const transactionId = await getNextTransactionId();
@@ -596,12 +574,7 @@ export async function configureSimpleServiceProxyOff({ domain }) {
export async function removeWwwRedirection(domain) { export async function removeWwwRedirection(domain) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy();
await checkHAProxy(haproxy);
} catch (error) {
return;
}
const rules: any = await haproxy const rules: any = await haproxy
.get(`v2/services/haproxy/configuration/http_request_rules`, { .get(`v2/services/haproxy/configuration/http_request_rules`, {
searchParams: { searchParams: {
@@ -631,11 +604,7 @@ export async function removeWwwRedirection(domain) {
} }
export async function setWwwRedirection(fqdn) { export async function setWwwRedirection(fqdn) {
const haproxy = await haproxyInstance(); const haproxy = await haproxyInstance();
try { await checkHAProxy(haproxy);
await checkHAProxy(haproxy);
} catch (error) {
return;
}
const transactionId = await getNextTransactionId(); const transactionId = await getNextTransactionId();
try { try {

View File

@@ -96,10 +96,9 @@
async function update() { async function update() {
updateStatus.loading = true; updateStatus.loading = true;
// if (!dev) {
try { try {
await post(`/update.json`, { type: 'update', latestVersion }); await post(`/update.json`, { type: 'update', latestVersion });
toast.push('Update completed. Waiting for the new version to start...'); toast.push('Update completed.<br>Waiting for the new version to start...');
let reachable = false; let reachable = false;
let tries = 0; let tries = 0;
do { do {
@@ -119,10 +118,9 @@
await asyncSleep(3000); await asyncSleep(3000);
return window.location.reload(); return window.location.reload();
} catch ({ error }) { } catch ({ error }) {
return errorNotification(error);
} finally {
updateStatus.success = false; updateStatus.success = false;
updateStatus.loading = false; updateStatus.loading = false;
return errorNotification(error);
} }
} }
</script> </script>

View File

@@ -15,16 +15,32 @@
let loading = true; let loading = true;
let currentStatus; let currentStatus;
let streamInterval; let streamInterval;
let followingBuild;
let followingInterval;
let logsEl;
const { id } = $page.params; const { id } = $page.params;
const cleanAnsiCodes = (str: string) => str.replace(/\x1B\[(\d+)m/g, '');
function followBuild() {
followingBuild = !followingBuild;
if (followingBuild) {
followingInterval = setInterval(() => {
logsEl.scrollTop = logsEl.scrollHeight;
window.scrollTo(0, document.body.scrollHeight);
}, 100);
} else {
window.clearInterval(followingInterval);
}
}
async function streamLogs(sequence = 0) { async function streamLogs(sequence = 0) {
try { try {
let { logs: responseLogs, status } = await get( let { logs: responseLogs, status } = await get(
`/applications/${id}/logs/build/build.json?buildId=${buildId}&sequence=${sequence}` `/applications/${id}/logs/build/build.json?buildId=${buildId}&sequence=${sequence}`
); );
currentStatus = status; currentStatus = status;
logs = logs.concat(responseLogs); logs = logs.concat(responseLogs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
loading = false; loading = false;
streamInterval = setInterval(async () => { streamInterval = setInterval(async () => {
if (status !== 'running') { if (status !== 'running') {
@@ -38,8 +54,13 @@
); );
status = data.status; status = data.status;
currentStatus = status; currentStatus = status;
logs = logs.concat(data.logs);
logs = logs.concat(data.logs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
dispatch('updateBuildStatus', { status }); dispatch('updateBuildStatus', { status });
if (followingBuild) {
const logEl = document.getElementById('logs');
logEl.scrollTop = logEl.scrollHeight;
}
} catch ({ error }) { } catch ({ error }) {
return errorNotification(error); return errorNotification(error);
} }
@@ -50,6 +71,7 @@
} }
onDestroy(() => { onDestroy(() => {
clearInterval(streamInterval); clearInterval(streamInterval);
clearInterval(followingInterval);
}); });
onMount(async () => { onMount(async () => {
window.scrollTo(0, 0); window.scrollTo(0, 0);
@@ -60,12 +82,37 @@
{#if loading} {#if loading}
<Loading /> <Loading />
{:else} {:else}
<div class="relative"> <div class="relative ">
{#if currentStatus === 'running'} {#if currentStatus === 'running'}
<LoadingLogs /> <LoadingLogs />
{/if} {/if}
<div class="flex justify-end sticky top-0 p-2">
<button
on:click={followBuild}
data-tooltip="Follow logs"
class:text-green-500={followingBuild}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="9" />
<line x1="8" y1="12" x2="12" y2="16" />
<line x1="12" y1="8" x2="12" y2="16" />
<line x1="16" y1="12" x2="12" y2="16" />
</svg>
</button>
</div>
<div <div
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words" class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 py-5 px-6 whitespace-pre-wrap break-words overflow-auto max-h-[80vh] -mt-12"
bind:this={logsEl}
> >
{#each logs as log} {#each logs as log}
<div>{log.line + '\n'}</div> <div>{log.line + '\n'}</div>

View File

@@ -33,7 +33,6 @@
export let buildCount; export let buildCount;
let buildId; let buildId;
$: buildId;
let skip = 0; let skip = 0;
let noMoreBuilds = buildCount < 5 || buildCount <= skip; let noMoreBuilds = buildCount < 5 || buildCount <= skip;
@@ -92,45 +91,47 @@
Build logs of <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a> Build logs of <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
</div> </div>
</div> </div>
<div class="flex flex-row justify-start space-x-2 px-10 pt-6 "> <div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
<div class="min-w-[16rem] space-y-2"> <div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
{#each builds as build (build.id)} <div class="top-4 md:sticky">
<div {#each builds as build (build.id)}
data-tooltip={new Intl.DateTimeFormat('default', dateOptions).format( <div
new Date(build.createdAt) data-tooltip={new Intl.DateTimeFormat('default', dateOptions).format(
) + `\n${build.status}`} new Date(build.createdAt)
on:click={() => loadBuild(build.id)} ) + `\n${build.status}`}
class="tooltip-top flex cursor-pointer items-center justify-center rounded-r border-l-2 border-transparent py-4 no-underline transition-all duration-100 hover:bg-coolgray-400 hover:shadow-xl" on:click={() => loadBuild(build.id)}
class:bg-coolgray-400={buildId === build.id} class="tooltip-top flex cursor-pointer items-center justify-center rounded-r border-l-2 border-transparent py-4 no-underline transition-all duration-100 hover:bg-coolgray-400 hover:shadow-xl "
class:border-red-500={build.status === 'failed'} class:bg-coolgray-400={buildId === build.id}
class:border-green-500={build.status === 'success'} class:border-red-500={build.status === 'failed'}
class:border-yellow-500={build.status === 'inprogress'} class:border-green-500={build.status === 'success'}
> class:border-yellow-500={build.status === 'inprogress'}
<div class="flex-col px-2"> >
<div class="text-sm font-bold"> <div class="flex-col px-2">
{application.branch} <div class="text-sm font-bold">
{application.branch}
</div>
<div class="text-xs">
{build.type}
</div>
</div> </div>
<div class="text-xs"> <div class="flex-1" />
{build.type}
</div>
</div>
<div class="flex-1" />
<div class="w-48 text-center text-xs"> <div class="w-48 text-center text-xs">
{#if build.status === 'running'} {#if build.status === 'running'}
<div class="font-bold">Running</div> <div class="font-bold">Running</div>
{:else} {:else}
<div>{build.since}</div> <div>{build.since}</div>
<div>Finished in <span class="font-bold">{build.took}s</span></div> <div>Finished in <span class="font-bold">{build.took}s</span></div>
{/if} {/if}
</div>
</div> </div>
</div> {/each}
{/each} </div>
{#if buildCount > 0 && !noMoreBuilds} <div class="flex space-x-2">
<button class="w-full" on:click={loadMoreBuilds}>Load More</button> <button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}>Load More</button>
{/if} </div>
</div> </div>
<div class="w-96 flex-1"> <div class="flex-1 md:w-96">
{#if buildId} {#if buildId}
{#key buildId} {#key buildId}
<svelte:component this={BuildLog} {buildId} on:updateBuildStatus={updateBuildStatus} /> <svelte:component this={BuildLog} {buildId} on:updateBuildStatus={updateBuildStatus} />

View File

@@ -27,19 +27,23 @@
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { get } from '$lib/api'; import { get } from '$lib/api';
import { errorNotification } from '$lib/form'; import { errorNotification } from '$lib/form';
let loadLogsInterval = null; let loadLogsInterval = null;
let logs = []; let logs = [];
let followingBuild;
let followingInterval;
let logsEl;
const { id } = $page.params; const { id } = $page.params;
onMount(async () => { onMount(async () => {
loadLogs(); loadLogs();
loadLogsInterval = setInterval(() => { loadLogsInterval = setInterval(() => {
loadLogs(); loadLogs();
}, 3000); }, 1000);
}); });
onDestroy(() => { onDestroy(() => {
clearInterval(loadLogsInterval); clearInterval(loadLogsInterval);
clearInterval(followingInterval);
}); });
async function loadLogs() { async function loadLogs() {
try { try {
@@ -50,6 +54,18 @@
return errorNotification(error); return errorNotification(error);
} }
} }
function followBuild() {
followingBuild = !followingBuild;
if (followingBuild) {
followingInterval = setInterval(() => {
logsEl.scrollTop = logsEl.scrollHeight;
window.scrollTo(0, document.body.scrollHeight);
}, 100);
} else {
window.clearInterval(followingInterval);
}
}
</script> </script>
<div class="flex space-x-1 p-6 font-bold"> <div class="flex space-x-1 p-6 font-bold">
@@ -63,8 +79,33 @@
{:else} {:else}
<div class="relative w-full"> <div class="relative w-full">
<LoadingLogs /> <LoadingLogs />
<div class="flex justify-end sticky top-0 p-2">
<button
on:click={followBuild}
data-tooltip="Follow logs"
class:text-green-500={followingBuild}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="9" />
<line x1="8" y1="12" x2="12" y2="16" />
<line x1="12" y1="8" x2="12" y2="16" />
<line x1="16" y1="12" x2="12" y2="16" />
</svg>
</button>
</div>
<div <div
class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 p-6 whitespace-pre-wrap break-words w-full" class="font-mono leading-6 text-left text-md tracking-tighter rounded bg-coolgray-200 p-6 whitespace-pre-wrap break-words w-full mb-10 -mt-12"
bind:this={logsEl}
> >
{#each logs as log} {#each logs as log}
{log + '\n'} {log + '\n'}

View File

@@ -15,8 +15,7 @@ export const post: RequestHandler = async (event) => {
status: 200 status: 200
}; };
} catch (error) { } catch (error) {
return ErrorHandler(error);
} finally {
await stopCoolifyProxy(engine); await stopCoolifyProxy(engine);
return ErrorHandler(error);
} }
}; };

View File

@@ -110,23 +110,23 @@
loading = false; loading = false;
} }
} }
onMount(async () => { // onMount(async () => {
if ( // if (
service.type && // service.type &&
service.destinationDockerId && // service.destinationDockerId &&
service.version && // service.version &&
service.fqdn && // service.fqdn &&
!isRunning // !isRunning
) { // ) {
try { // try {
await post(`/services/${service.id}/${service.type}/stop.json`, {}); // await post(`/services/${service.id}/${service.type}/stop.json`, {});
} catch ({ error }) { // } catch ({ error }) {
return errorNotification(error); // return errorNotification(error);
} finally { // } finally {
loading = false; // loading = false;
} // }
} // }
}); // });
</script> </script>
<nav class="nav-side"> <nav class="nav-side">

View File

@@ -5,6 +5,7 @@ import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { import {
checkHAProxy,
configureSimpleServiceProxyOn, configureSimpleServiceProxyOn,
reloadHaproxy, reloadHaproxy,
setWwwRedirection, setWwwRedirection,
@@ -22,6 +23,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { const {
type, type,

View File

@@ -33,8 +33,12 @@ export const post: RequestHandler = async (event) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await stopTcpHttpProxy(destinationDocker, publicPort); try {
await configureSimpleServiceProxyOff({ domain }); await stopTcpHttpProxy(destinationDocker, publicPort);
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {

View File

@@ -4,7 +4,12 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { configureSimpleServiceProxyOn, reloadHaproxy, setWwwRedirection } from '$lib/haproxy'; import {
checkHAProxy,
configureSimpleServiceProxyOn,
reloadHaproxy,
setWwwRedirection
} from '$lib/haproxy';
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
@@ -15,6 +20,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { type, version, fqdn, destinationDockerId, destinationDocker } = service; const { type, version, fqdn, destinationDockerId, destinationDocker } = service;
@@ -58,7 +64,6 @@ export const post: RequestHandler = async (event) => {
status: 200 status: 200
}; };
} catch (error) { } catch (error) {
console.log(error);
return ErrorHandler(error); return ErrorHandler(error);
} }
} catch (error) { } catch (error) {

View File

@@ -27,7 +27,11 @@ export const post: RequestHandler = async (event) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await configureSimpleServiceProxyOff({ domain }); try {
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {

View File

@@ -4,7 +4,12 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { configureSimpleServiceProxyOn, reloadHaproxy, setWwwRedirection } from '$lib/haproxy'; import {
checkHAProxy,
configureSimpleServiceProxyOn,
reloadHaproxy,
setWwwRedirection
} from '$lib/haproxy';
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
@@ -15,6 +20,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { const {
type, type,

View File

@@ -37,7 +37,11 @@ export const post: RequestHandler = async (event) => {
console.error(error); console.error(error);
} }
await configureSimpleServiceProxyOff({ domain }); try {
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {

View File

@@ -4,7 +4,12 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { configureSimpleServiceProxyOn, reloadHaproxy, setWwwRedirection } from '$lib/haproxy'; import {
checkHAProxy,
configureSimpleServiceProxyOn,
reloadHaproxy,
setWwwRedirection
} from '$lib/haproxy';
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { getServiceImage, ErrorHandler } from '$lib/database'; import { getServiceImage, ErrorHandler } from '$lib/database';
@@ -15,6 +20,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { type, version, fqdn, destinationDockerId, destinationDocker } = service; const { type, version, fqdn, destinationDockerId, destinationDocker } = service;

View File

@@ -27,7 +27,11 @@ export const post: RequestHandler = async (event) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await configureSimpleServiceProxyOff({ domain }); try {
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {

View File

@@ -4,7 +4,12 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { configureSimpleServiceProxyOn, reloadHaproxy, setWwwRedirection } from '$lib/haproxy'; import {
checkHAProxy,
configureSimpleServiceProxyOn,
reloadHaproxy,
setWwwRedirection
} from '$lib/haproxy';
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
@@ -15,6 +20,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { const {
type, type,

View File

@@ -27,7 +27,11 @@ export const post: RequestHandler = async (event) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await configureSimpleServiceProxyOff({ domain }); try {
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {
status: 200 status: 200

View File

@@ -4,7 +4,12 @@ import { promises as fs } from 'fs';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import type { RequestHandler } from '@sveltejs/kit'; import type { RequestHandler } from '@sveltejs/kit';
import { letsEncrypt } from '$lib/letsencrypt'; import { letsEncrypt } from '$lib/letsencrypt';
import { configureSimpleServiceProxyOn, reloadHaproxy, setWwwRedirection } from '$lib/haproxy'; import {
checkHAProxy,
configureSimpleServiceProxyOn,
reloadHaproxy,
setWwwRedirection
} from '$lib/haproxy';
import { getDomain } from '$lib/components/common'; import { getDomain } from '$lib/components/common';
import { ErrorHandler } from '$lib/database'; import { ErrorHandler } from '$lib/database';
@@ -15,6 +20,7 @@ export const post: RequestHandler = async (event) => {
const { id } = event.params; const { id } = event.params;
try { try {
await checkHAProxy();
const service = await db.getService({ id, teamId }); const service = await db.getService({ id, teamId });
const { const {
type, type,

View File

@@ -30,7 +30,11 @@ export const post: RequestHandler = async (event) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
await configureSimpleServiceProxyOff({ domain }); try {
await configureSimpleServiceProxyOff({ domain });
} catch (error) {
console.log(error);
}
} }
return { return {

View File

@@ -36,8 +36,6 @@ export const post: RequestHandler = async (event) => {
body: {} body: {}
}; };
} else { } else {
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
await asyncSleep(2000);
return { return {
status: 200, status: 200,
body: {} body: {}

View File

@@ -5,6 +5,7 @@ import cuid from 'cuid';
import crypto from 'crypto'; import crypto from 'crypto';
import { buildQueue } from '$lib/queues'; import { buildQueue } from '$lib/queues';
import { checkContainer, removeProxyConfiguration } from '$lib/haproxy'; import { checkContainer, removeProxyConfiguration } from '$lib/haproxy';
import { dev } from '$app/env';
export const options: RequestHandler = async () => { export const options: RequestHandler = async () => {
return { return {
@@ -22,8 +23,8 @@ export const post: RequestHandler = async (event) => {
const buildId = cuid(); const buildId = cuid();
const allowedGithubEvents = ['push', 'pull_request']; const allowedGithubEvents = ['push', 'pull_request'];
const allowedActions = ['opened', 'reopened', 'synchronize', 'closed']; const allowedActions = ['opened', 'reopened', 'synchronize', 'closed'];
const githubEvent = event.request.headers.get('x-github-event').toLowerCase(); const githubEvent = event.request.headers.get('x-github-event')?.toLowerCase();
const githubSignature = event.request.headers.get('x-hub-signature-256').toLowerCase(); const githubSignature = event.request.headers.get('x-hub-signature-256')?.toLowerCase();
if (!allowedGithubEvents.includes(githubEvent)) { if (!allowedGithubEvents.includes(githubEvent)) {
return { return {
status: 500, status: 500,
@@ -34,7 +35,6 @@ export const post: RequestHandler = async (event) => {
} }
let repository, projectId, branch; let repository, projectId, branch;
const body = await event.request.json(); const body = await event.request.json();
if (githubEvent === 'push') { if (githubEvent === 'push') {
repository = body.repository; repository = body.repository;
projectId = repository.id; projectId = repository.id;
@@ -54,14 +54,17 @@ export const post: RequestHandler = async (event) => {
'utf8' 'utf8'
); );
const checksum = Buffer.from(githubSignature, 'utf8'); const checksum = Buffer.from(githubSignature, 'utf8');
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) { if (!dev) {
return { if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
status: 500, return {
body: { status: 500,
message: 'SHA256 checksum failed. Are you doing something fishy?' body: {
} message: 'SHA256 checksum failed. Are you doing something fishy?'
}; }
};
}
} }
if (githubEvent === 'push') { if (githubEvent === 'push') {
if (!applicationFound.configHash) { if (!applicationFound.configHash) {
const configHash = crypto const configHash = crypto
@@ -120,7 +123,11 @@ export const post: RequestHandler = async (event) => {
}; };
} }
} }
if (pullmergeRequestAction === 'opened' || pullmergeRequestAction === 'reopened') { if (
pullmergeRequestAction === 'opened' ||
pullmergeRequestAction === 'reopened' ||
pullmergeRequestAction === 'synchronize'
) {
await buildQueue.add(buildId, { await buildQueue.add(buildId, {
build_id: buildId, build_id: buildId,
type: 'webhook_pr', type: 'webhook_pr',

View File

@@ -69,7 +69,7 @@ a {
} }
.nav-side { .nav-side {
@apply absolute right-0 top-0 z-50 m-5 flex items-center justify-end space-x-2 bg-coolblack/40 text-white; @apply relative right-0 top-0 z-50 m-5 flex flex-wrap items-center justify-end space-x-2 bg-coolblack/40 text-white sm:absolute;
} }
.add-icon { .add-icon {