commit
1881e646d4
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
/yarn.lock
|
||||
|
||||
.env
|
||||
.env.prod
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coolify",
|
||||
"description": "An open-source & self-hostable Heroku / Netlify alternative.",
|
||||
"version": "2.0.10",
|
||||
"version": "2.0.11",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "docker compose -f docker-compose-dev.yaml up -d && NODE_ENV=development svelte-kit dev --host 0.0.0.0",
|
||||
|
@ -59,11 +59,15 @@ export async function removeApplication({ id, teamId }) {
|
||||
const id = containerObj.ID;
|
||||
const preview = containerObj.Image.split('-')[1];
|
||||
await removeDestinationDocker({ id, engine: destinationDocker.engine });
|
||||
try {
|
||||
if (preview) {
|
||||
await removeProxyConfiguration({ domain: `${preview}.${domain}` });
|
||||
} else {
|
||||
await removeProxyConfiguration({ domain });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +96,9 @@
|
||||
|
||||
async function update() {
|
||||
updateStatus.loading = true;
|
||||
// if (!dev) {
|
||||
try {
|
||||
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 tries = 0;
|
||||
do {
|
||||
@ -119,10 +118,9 @@
|
||||
await asyncSleep(3000);
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
updateStatus.success = false;
|
||||
updateStatus.loading = false;
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -15,16 +15,32 @@
|
||||
let loading = true;
|
||||
let currentStatus;
|
||||
let streamInterval;
|
||||
let followingBuild;
|
||||
let followingInterval;
|
||||
let logsEl;
|
||||
|
||||
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) {
|
||||
try {
|
||||
let { logs: responseLogs, status } = await get(
|
||||
`/applications/${id}/logs/build/build.json?buildId=${buildId}&sequence=${sequence}`
|
||||
);
|
||||
currentStatus = status;
|
||||
logs = logs.concat(responseLogs);
|
||||
logs = logs.concat(responseLogs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
|
||||
loading = false;
|
||||
streamInterval = setInterval(async () => {
|
||||
if (status !== 'running') {
|
||||
@ -38,8 +54,13 @@
|
||||
);
|
||||
status = data.status;
|
||||
currentStatus = status;
|
||||
logs = logs.concat(data.logs);
|
||||
|
||||
logs = logs.concat(data.logs.map((log) => ({ ...log, line: cleanAnsiCodes(log.line) })));
|
||||
dispatch('updateBuildStatus', { status });
|
||||
if (followingBuild) {
|
||||
const logEl = document.getElementById('logs');
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
@ -50,6 +71,7 @@
|
||||
}
|
||||
onDestroy(() => {
|
||||
clearInterval(streamInterval);
|
||||
clearInterval(followingInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
window.scrollTo(0, 0);
|
||||
@ -64,8 +86,33 @@
|
||||
{#if currentStatus === 'running'}
|
||||
<LoadingLogs />
|
||||
{/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
|
||||
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}
|
||||
<div>{log.line + '\n'}</div>
|
||||
|
@ -33,7 +33,6 @@
|
||||
export let buildCount;
|
||||
|
||||
let buildId;
|
||||
$: buildId;
|
||||
|
||||
let skip = 0;
|
||||
let noMoreBuilds = buildCount < 5 || buildCount <= skip;
|
||||
@ -92,8 +91,9 @@
|
||||
Build logs of <a href={application.fqdn} target="_blank">{getDomain(application.fqdn)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-start space-x-2 px-10 pt-6 ">
|
||||
<div class="min-w-[16rem] space-y-2">
|
||||
<div class="block flex-row justify-start space-x-2 px-5 pt-6 sm:px-10 md:flex">
|
||||
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
|
||||
<div class="top-4 md:sticky">
|
||||
{#each builds as build (build.id)}
|
||||
<div
|
||||
data-tooltip={new Intl.DateTimeFormat('default', dateOptions).format(
|
||||
@ -126,11 +126,12 @@
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{#if buildCount > 0 && !noMoreBuilds}
|
||||
<button class="w-full" on:click={loadMoreBuilds}>Load More</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="w-96 flex-1">
|
||||
<div class="flex space-x-2">
|
||||
<button disabled={noMoreBuilds} class="w-full" on:click={loadMoreBuilds}>Load More</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 md:w-96">
|
||||
{#if buildId}
|
||||
{#key buildId}
|
||||
<svelte:component this={BuildLog} {buildId} on:updateBuildStatus={updateBuildStatus} />
|
||||
|
@ -27,19 +27,23 @@
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { get } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
|
||||
let loadLogsInterval = null;
|
||||
let logs = [];
|
||||
let followingBuild;
|
||||
let followingInterval;
|
||||
let logsEl;
|
||||
|
||||
const { id } = $page.params;
|
||||
|
||||
onMount(async () => {
|
||||
loadLogs();
|
||||
loadLogsInterval = setInterval(() => {
|
||||
loadLogs();
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
});
|
||||
onDestroy(() => {
|
||||
clearInterval(loadLogsInterval);
|
||||
clearInterval(followingInterval);
|
||||
});
|
||||
async function loadLogs() {
|
||||
try {
|
||||
@ -50,6 +54,18 @@
|
||||
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>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
@ -63,8 +79,33 @@
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<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
|
||||
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}
|
||||
{log + '\n'}
|
||||
|
@ -36,8 +36,6 @@ export const post: RequestHandler = async (event) => {
|
||||
body: {}
|
||||
};
|
||||
} else {
|
||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
||||
await asyncSleep(2000);
|
||||
return {
|
||||
status: 200,
|
||||
body: {}
|
||||
|
@ -5,6 +5,7 @@ import cuid from 'cuid';
|
||||
import crypto from 'crypto';
|
||||
import { buildQueue } from '$lib/queues';
|
||||
import { checkContainer, removeProxyConfiguration } from '$lib/haproxy';
|
||||
import { dev } from '$app/env';
|
||||
|
||||
export const options: RequestHandler = async () => {
|
||||
return {
|
||||
@ -22,8 +23,8 @@ export const post: RequestHandler = async (event) => {
|
||||
const buildId = cuid();
|
||||
const allowedGithubEvents = ['push', 'pull_request'];
|
||||
const allowedActions = ['opened', 'reopened', 'synchronize', 'closed'];
|
||||
const githubEvent = event.request.headers.get('x-github-event').toLowerCase();
|
||||
const githubSignature = event.request.headers.get('x-hub-signature-256').toLowerCase();
|
||||
const githubEvent = event.request.headers.get('x-github-event')?.toLowerCase();
|
||||
const githubSignature = event.request.headers.get('x-hub-signature-256')?.toLowerCase();
|
||||
if (!allowedGithubEvents.includes(githubEvent)) {
|
||||
return {
|
||||
status: 500,
|
||||
@ -34,7 +35,6 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
let repository, projectId, branch;
|
||||
const body = await event.request.json();
|
||||
|
||||
if (githubEvent === 'push') {
|
||||
repository = body.repository;
|
||||
projectId = repository.id;
|
||||
@ -54,6 +54,7 @@ export const post: RequestHandler = async (event) => {
|
||||
'utf8'
|
||||
);
|
||||
const checksum = Buffer.from(githubSignature, 'utf8');
|
||||
if (!dev) {
|
||||
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
|
||||
return {
|
||||
status: 500,
|
||||
@ -62,6 +63,8 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (githubEvent === 'push') {
|
||||
if (!applicationFound.configHash) {
|
||||
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, {
|
||||
build_id: buildId,
|
||||
type: 'webhook_pr',
|
||||
|
@ -69,7 +69,7 @@ a {
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user