feat: docker compose
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
|
||||
<div class="dropdown dropdown-bottom">
|
||||
<slot>
|
||||
<label for="new" tabindex="0" class="btn btn-sm text-sm bg-coollabs hover:bg-coollabs-100">
|
||||
<label for="new" tabindex="0" class="btn btn-sm text-sm bg-coollabs hover:bg-coollabs-100 w-52">
|
||||
<svg
|
||||
class="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<ul id="new" tabindex="0" class="dropdown-content menu p-2 shadow bg-coolgray-300 rounded w-52">
|
||||
<li>
|
||||
<button on:click={newApplication} class="no-underline hover:bg-applications rounded-none ">
|
||||
<button on:click={newApplication} class="no-underline hover:bg-applications tracking-wide font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
@@ -58,7 +58,7 @@
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={newService} class="no-underline hover:bg-services rounded-none ">
|
||||
<button on:click={newService} class="no-underline hover:bg-services tracking-wide font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
@@ -75,7 +75,7 @@
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={newDatabase} class="no-underline hover:bg-databases rounded-none ">
|
||||
<button on:click={newDatabase} class="no-underline hover:bg-databases tracking-wide font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
@@ -94,7 +94,7 @@
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/sources/new" class="no-underline hover:bg-sources rounded-none ">
|
||||
<a href="/sources/new" class="no-underline hover:bg-sources tracking-wide font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
@@ -116,7 +116,7 @@
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/destinations/new" class="no-underline hover:bg-destinations rounded-none ">
|
||||
<a href="/destinations/new" class="no-underline hover:bg-destinations tracking-wide font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
|
@@ -160,12 +160,12 @@
|
||||
<span>Logs</span>
|
||||
</li>
|
||||
<li
|
||||
class:text-stone-600={!$status.application.isRunning}
|
||||
class:text-stone-600={$status.application.overallStatus !== 'healthy'}
|
||||
class="rounded"
|
||||
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/logs`}
|
||||
>
|
||||
<a
|
||||
href={$status.application.isRunning ? `/applications/${$page.params.id}/logs` : ''}
|
||||
href={$status.application.overallStatus === 'healthy' ? `/applications/${$page.params.id}/logs` : ''}
|
||||
class="no-underline w-full"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -218,10 +218,10 @@
|
||||
</li>
|
||||
<li
|
||||
class="rounded"
|
||||
class:text-stone-600={!$status.application.isRunning}
|
||||
class:text-stone-600={$status.application.overallStatus !== 'healthy'}
|
||||
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/usage`}
|
||||
>
|
||||
<a href={$status.application.isRunning ? `/applications/${$page.params.id}/usage` : ''} class="no-underline w-full"
|
||||
<a href={$status.application.overallStatus === 'healthy' ? `/applications/${$page.params.id}/usage` : ''} class="no-underline w-full"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
|
@@ -153,10 +153,16 @@
|
||||
const data = await get(`/applications/${id}/status`);
|
||||
|
||||
$status.application.statuses = data;
|
||||
const numberOfApplications =
|
||||
application.buildPack === 'compose'
|
||||
? Object.entries(JSON.parse(application.dockerComposeConfiguration)).length
|
||||
: 1;
|
||||
let numberOfApplications = 0;
|
||||
if (application.dockerComposeConfiguration) {
|
||||
numberOfApplications =
|
||||
application.buildPack === 'compose'
|
||||
? Object.entries(JSON.parse(application.dockerComposeConfiguration)).length
|
||||
: 1;
|
||||
} else {
|
||||
numberOfApplications = 1;
|
||||
}
|
||||
|
||||
if ($status.application.statuses.length === 0) {
|
||||
$status.application.overallStatus = 'stopped';
|
||||
} else {
|
||||
@@ -187,9 +193,6 @@
|
||||
|
||||
onDestroy(() => {
|
||||
$status.application.initialLoading = true;
|
||||
// $status.application.isRunning = false;
|
||||
// $status.application.isExited = false;
|
||||
// $status.application.isRestarting = false;
|
||||
$status.application.loading = false;
|
||||
$location = null;
|
||||
$isDeploymentEnabled = false;
|
||||
@@ -197,9 +200,6 @@
|
||||
});
|
||||
onMount(async () => {
|
||||
setLocation(application, settings);
|
||||
// $status.application.isRunning = false;
|
||||
// $status.application.isExited = false;
|
||||
// $status.application.isRestarting = false;
|
||||
$status.application.loading = false;
|
||||
if ($isDeploymentEnabled) {
|
||||
await getStatus();
|
||||
@@ -321,6 +321,50 @@
|
||||
Loading...
|
||||
</button>
|
||||
{:else if $status.application.overallStatus === 'healthy'}
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm btn-error gap-2"
|
||||
>
|
||||
<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" />
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||
</svg> Stop
|
||||
</button>
|
||||
{#if application.buildPack !== 'compose'}
|
||||
<button
|
||||
on:click={restartApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<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" />
|
||||
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" />
|
||||
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
|
||||
</svg> Restart
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
@@ -345,51 +389,9 @@
|
||||
|
||||
Force Redeploy
|
||||
</button>
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm btn-error gap-2"
|
||||
>
|
||||
<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" />
|
||||
<rect x="6" y="5" width="4" height="14" rx="1" />
|
||||
<rect x="14" y="5" width="4" height="14" rx="1" />
|
||||
</svg> Stop
|
||||
</button>
|
||||
|
||||
|
||||
{#if application.buildPack !== 'compose'}
|
||||
<button
|
||||
on:click={restartApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<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" />
|
||||
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4" />
|
||||
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4" />
|
||||
</svg> Restart
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{:else if $isDeploymentEnabled && !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
|
||||
<button
|
||||
class="btn btn-sm gap-2 btn-primary"
|
||||
@@ -412,9 +414,8 @@
|
||||
{$status.application.overallStatus === 'degraded' ? 'Restart Degraded Services' : 'Deploy'}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if $location && $status.application.overallStatus === 'healthy'}
|
||||
<a id="openApplication" href={$location} target="_blank" class="icons bg-transparent "
|
||||
<a href={$location} target="_blank" class="btn btn-sm gap-2 text-sm bg-primary"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
@@ -429,9 +430,8 @@
|
||||
<path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" />
|
||||
<line x1="10" y1="14" x2="20" y2="4" />
|
||||
<polyline points="15 4 20 4 20 9" />
|
||||
</svg></a
|
||||
</svg>Open</a
|
||||
>
|
||||
<Tooltip triggeredBy="#openApplication">Open Application</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -14,8 +14,9 @@
|
||||
export let foundConfig: any;
|
||||
export let scanning: any;
|
||||
export let packageManager: any;
|
||||
export let dockerComposeFile: any = null;
|
||||
export let dockerComposeFile: string | null = null;
|
||||
export let dockerComposeFileLocation: string | null = null;
|
||||
export let dockerComposeConfiguration: any = null;
|
||||
|
||||
async function handleSubmit(name: string) {
|
||||
try {
|
||||
@@ -27,11 +28,19 @@
|
||||
delete tempBuildPack.fancyName;
|
||||
delete tempBuildPack.color;
|
||||
delete tempBuildPack.hoverColor;
|
||||
let composeConfiguration: any = {}
|
||||
if (!dockerComposeConfiguration && dockerComposeFile) {
|
||||
for (const [name, _] of Object.entries(JSON.parse(dockerComposeFile).services)) {
|
||||
composeConfiguration[name] = {};
|
||||
}
|
||||
|
||||
}
|
||||
await post(`/applications/${id}`, {
|
||||
...tempBuildPack,
|
||||
buildPack: name,
|
||||
dockerComposeFile,
|
||||
dockerComposeFileLocation
|
||||
dockerComposeFileLocation,
|
||||
dockerComposeConfiguration: JSON.stringify(composeConfiguration) || JSON.stringify({})
|
||||
});
|
||||
await post(`/applications/${id}/configuration/buildpack`, { buildPack: name });
|
||||
return await goto(from || `/applications/${id}`);
|
||||
|
@@ -165,7 +165,7 @@
|
||||
placeholder="eg: https://github.com/coollabsio/nodejs-example/tree/main"
|
||||
bind:value={publicRepositoryLink}
|
||||
/>
|
||||
<button class="btn bg-orange-600" class:loading={loading.branches} type="submit">
|
||||
<button class="btn bg-orange-600" type="submit">
|
||||
Load Repository
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -12,6 +12,7 @@
|
||||
const response = await get(`/applications/${params.id}/configuration/buildpack`);
|
||||
return {
|
||||
props: {
|
||||
application,
|
||||
...response
|
||||
}
|
||||
};
|
||||
@@ -25,6 +26,14 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let apiUrl: any;
|
||||
export let projectId: any;
|
||||
export let repository: any;
|
||||
export let branch: any;
|
||||
export let type: any;
|
||||
export let application: any;
|
||||
export let isPublicRepository: boolean;
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { page } from '$app/stores';
|
||||
@@ -41,16 +50,9 @@
|
||||
let scanning: boolean = true;
|
||||
let foundConfig: any = null;
|
||||
let packageManager: string = 'npm';
|
||||
let dockerComposeFile: any = null;
|
||||
let dockerComposeFileLocation: string | null = null;
|
||||
|
||||
export let apiUrl: any;
|
||||
export let projectId: any;
|
||||
export let repository: any;
|
||||
export let branch: any;
|
||||
export let type: any;
|
||||
export let application: any;
|
||||
export let isPublicRepository: boolean;
|
||||
let dockerComposeFile: string | null = application.dockerComposeFile || null;
|
||||
let dockerComposeFileLocation: string | null = application.dockerComposeFileLocation || null;
|
||||
let dockerComposeConfiguration: any = application.dockerComposeConfiguration || null;
|
||||
|
||||
function checkPackageJSONContents({ key, json }: { key: any; json: any }) {
|
||||
return json?.dependencies?.hasOwnProperty(key) || json?.devDependencies?.hasOwnProperty(key);
|
||||
@@ -224,7 +226,8 @@
|
||||
);
|
||||
if (data?.content) {
|
||||
const content = atob(data.content);
|
||||
dockerComposeFile = JSON.stringify(yaml.load(content) || null);
|
||||
const dockerComposeJson = yaml.load(content) || null;
|
||||
dockerComposeFile = JSON.stringify(dockerComposeJson);
|
||||
dockerComposeFileLocation = dockerComposeFileYml
|
||||
? 'docker-compose.yml'
|
||||
: 'docker-compose.yaml';
|
||||
@@ -309,12 +312,7 @@
|
||||
<div class="flex flex-wrap justify-center">
|
||||
{#each buildPacks.filter((bp) => bp.isHerokuBuildPack === true) as buildPack}
|
||||
<div class="p-2">
|
||||
<BuildPack
|
||||
{packageManager}
|
||||
{buildPack}
|
||||
{scanning}
|
||||
bind:foundConfig
|
||||
/>
|
||||
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -331,6 +329,7 @@
|
||||
bind:foundConfig
|
||||
{dockerComposeFile}
|
||||
{dockerComposeFileLocation}
|
||||
{dockerComposeConfiguration}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -341,12 +340,7 @@
|
||||
<div class="flex flex-wrap justify-center">
|
||||
{#each buildPacks.filter((bp) => bp.isCoolifyBuildPack === true && bp.type === 'specific') as buildPack}
|
||||
<div class="p-2">
|
||||
<BuildPack
|
||||
{packageManager}
|
||||
{buildPack}
|
||||
{scanning}
|
||||
bind:foundConfig
|
||||
/>
|
||||
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@@ -59,6 +59,7 @@
|
||||
$status.application.overallStatus === 'healthy' ||
|
||||
$status.application.initialLoading;
|
||||
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
let statues: any = {};
|
||||
let loading = false;
|
||||
let fqdnEl: any = null;
|
||||
@@ -377,7 +378,7 @@
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<form on:submit|preventDefault={() => handleSubmit()}>
|
||||
<div class="mx-auto w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
|
||||
<div class="title font-bold pb-3 ">General</div>
|
||||
@@ -440,7 +441,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="buildPack">{$t('application.build_pack')} </label>
|
||||
{#if isDisabled}
|
||||
<input class="uppercase w-full" disabled={isDisabled} value={application.buildPack} />
|
||||
<input class="capitalize w-full" disabled={isDisabled} value={application.buildPack} />
|
||||
{:else}
|
||||
<a
|
||||
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
|
||||
@@ -914,10 +915,8 @@
|
||||
<div class="title font-bold pb-3 pt-10 border-b border-coolgray-500 mb-6">
|
||||
Stack <Beta />
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
on:click|preventDefault={reloadCompose}
|
||||
disabled={loading}>Reload Docker Compose File</button
|
||||
<button class="btn btn-sm btn-primary" on:click|preventDefault={reloadCompose}
|
||||
>Reload Docker Compose File</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
@@ -3,11 +3,11 @@
|
||||
import { get } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import LoadingLogs from '$lib/components/LoadingLogs.svelte';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import { status } from '$lib/store';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let application: any = {};
|
||||
let logsLoading = false;
|
||||
let loadLogsInterval: any = null;
|
||||
@@ -17,26 +17,39 @@
|
||||
let followingLogs: any;
|
||||
let logsEl: any;
|
||||
let position = 0;
|
||||
if (
|
||||
!$status.application.isExited &&
|
||||
!$status.application.isRestarting &&
|
||||
!$status.application.isRunning
|
||||
) {
|
||||
goto(`/applications/${$page.params.id}/`, { replaceState: true });
|
||||
}
|
||||
let services: any = [];
|
||||
let selectedService: any = null;
|
||||
const { id } = $page.params;
|
||||
onMount(async () => {
|
||||
const response = await get(`/applications/${id}`);
|
||||
application = response.application;
|
||||
loadAllLogs();
|
||||
loadLogsInterval = setInterval(() => {
|
||||
loadLogs();
|
||||
}, 1000);
|
||||
if (response.application.dockerComposeFile) {
|
||||
services = normalizeDockerServices(
|
||||
JSON.parse(response.application.dockerComposeFile).services
|
||||
);
|
||||
} else {
|
||||
services = [
|
||||
{
|
||||
name: ''
|
||||
}
|
||||
];
|
||||
await selectService('')
|
||||
}
|
||||
});
|
||||
onDestroy(() => {
|
||||
clearInterval(loadLogsInterval);
|
||||
clearInterval(followingInterval);
|
||||
});
|
||||
function normalizeDockerServices(services: any[]) {
|
||||
const tempdockerComposeServices = [];
|
||||
for (const [name, data] of Object.entries(services)) {
|
||||
tempdockerComposeServices.push({
|
||||
name,
|
||||
data
|
||||
});
|
||||
}
|
||||
return tempdockerComposeServices;
|
||||
}
|
||||
async function loadAllLogs() {
|
||||
try {
|
||||
logsLoading = true;
|
||||
@@ -55,7 +68,7 @@
|
||||
if (logsLoading) return;
|
||||
try {
|
||||
const newLogs: any = await get(
|
||||
`/applications/${id}/logs?since=${lastLog?.split(' ')[0] || 0}`
|
||||
`/applications/${id}/logs/${selectedService}?since=${lastLog?.split(' ')[0] || 0}`
|
||||
);
|
||||
|
||||
if (newLogs?.logs && newLogs.logs[newLogs.logs.length - 1] !== logs[logs.length - 1]) {
|
||||
@@ -89,6 +102,22 @@
|
||||
clearInterval(followingInterval);
|
||||
}
|
||||
}
|
||||
async function selectService(service: any, init: boolean = false) {
|
||||
if (services.length === 1 && init) return
|
||||
|
||||
if (loadLogsInterval) clearInterval(loadLogsInterval);
|
||||
if (followingInterval) clearInterval(followingInterval);
|
||||
|
||||
logs = [];
|
||||
lastLog = null;
|
||||
followingLogs = false;
|
||||
|
||||
selectedService = `${application.id}${service.name ? `-${service.name}` : ''}`;
|
||||
loadLogs();
|
||||
loadLogsInterval = setInterval(() => {
|
||||
loadLogs();
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mx-auto w-full">
|
||||
@@ -96,50 +125,67 @@
|
||||
<div class="title font-bold pb-3">Application Logs</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-center space-x-2">
|
||||
{#if logs.length === 0}
|
||||
<div class="text-xl font-bold tracking-tighter">{$t('application.build.waiting_logs')}</div>
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<div class="flex justify-start sticky space-x-2 pb-2">
|
||||
<button
|
||||
on:click={followBuild}
|
||||
class="btn btn-sm bg-coollabs"
|
||||
class:bg-coolgray-300={followingLogs}
|
||||
class:text-applications={followingLogs}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 mr-2"
|
||||
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>
|
||||
{followingLogs ? 'Following Logs...' : 'Follow Logs'}
|
||||
</button>
|
||||
{#if loadLogsInterval}
|
||||
<button id="streaming" class="btn btn-sm bg-transparent border-none loading" />
|
||||
<Tooltip triggeredBy="#streaming">Streaming logs</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
bind:this={logsEl}
|
||||
on:scroll={detect}
|
||||
class="font-mono w-full bg-coolgray-100 border border-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1"
|
||||
>
|
||||
{#each logs as log}
|
||||
<p>{log + '\n'}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex gap-2 lg:gap-8 pb-4">
|
||||
{#each services as service}
|
||||
<button
|
||||
on:click={() => selectService(service, true)}
|
||||
class:bg-primary={selectedService ===
|
||||
`${application.id}${service.name ? `-${service.name}` : ''}`}
|
||||
class:bg-coolgray-200={selectedService !==
|
||||
`${application.id}${service.name ? `-${service.name}` : ''}`}
|
||||
class="w-full rounded p-5 hover:bg-primary font-bold"
|
||||
>
|
||||
Container: {application.id}{service.name ? `-${service.name}` : ''}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if selectedService}
|
||||
<div class="flex flex-row justify-center space-x-2">
|
||||
{#if logs.length === 0}
|
||||
<div class="text-xl font-bold tracking-tighter">{$t('application.build.waiting_logs')}</div>
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<div class="flex justify-start sticky space-x-2 pb-2">
|
||||
<button
|
||||
on:click={followBuild}
|
||||
class="btn btn-sm bg-coollabs"
|
||||
class:bg-coolgray-300={followingLogs}
|
||||
class:text-applications={followingLogs}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 mr-2"
|
||||
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>
|
||||
{followingLogs ? 'Following Logs...' : 'Follow Logs'}
|
||||
</button>
|
||||
{#if loadLogsInterval}
|
||||
<button id="streaming" class="btn btn-sm bg-transparent border-none loading" />
|
||||
<Tooltip triggeredBy="#streaming">Streaming logs</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
bind:this={logsEl}
|
||||
on:scroll={detect}
|
||||
class="font-mono w-full bg-coolgray-100 border border-coolgray-200 p-5 overflow-x-auto overflox-y-auto max-h-[80vh] rounded mb-20 flex flex-col scrollbar-thumb-coollabs scrollbar-track-coolgray-200 scrollbar-w-1"
|
||||
>
|
||||
{#each logs as log}
|
||||
<p>{log + '\n'}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
Reference in New Issue
Block a user