ui: fixes
feat: restart coolify button
This commit is contained in:
@@ -73,6 +73,23 @@ export async function update(request: FastifyRequest<Update>) {
|
|||||||
return errorHandler({ status, message })
|
return errorHandler({ status, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export async function restartCoolify(request: FastifyRequest<any>) {
|
||||||
|
try {
|
||||||
|
const teamId = request.user.teamId;
|
||||||
|
if (teamId === '0') {
|
||||||
|
if (!isDev) {
|
||||||
|
await asyncExecShell(`docker restart coolify`);
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
console.log('Restarting Coolify')
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw { status: 500, message: 'You are not authorized to restart Coolify.' };
|
||||||
|
} catch ({ status, message }) {
|
||||||
|
return errorHandler({ status, message })
|
||||||
|
}
|
||||||
|
}
|
||||||
export async function showUsage() {
|
export async function showUsage() {
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { FastifyPluginAsync } from 'fastify';
|
import { FastifyPluginAsync } from 'fastify';
|
||||||
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser, cleanupManually } from './handlers';
|
import { checkUpdate, login, showDashboard, update, showUsage, getCurrentUser, cleanupManually, restartCoolify } from './handlers';
|
||||||
import { GetCurrentUser } from './types';
|
import { GetCurrentUser } from './types';
|
||||||
|
|
||||||
export interface Update {
|
export interface Update {
|
||||||
@@ -47,6 +47,10 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
|
|||||||
onRequest: [fastify.authenticate]
|
onRequest: [fastify.authenticate]
|
||||||
}, async () => await showUsage());
|
}, async () => await showUsage());
|
||||||
|
|
||||||
|
fastify.post('/internal/restart', {
|
||||||
|
onRequest: [fastify.authenticate]
|
||||||
|
}, async (request) => await restartCoolify(request));
|
||||||
|
|
||||||
fastify.post('/internal/cleanup', {
|
fastify.post('/internal/cleanup', {
|
||||||
onRequest: [fastify.authenticate]
|
onRequest: [fastify.authenticate]
|
||||||
}, async () => await cleanupManually());
|
}, async () => await cleanupManually());
|
||||||
|
@@ -20,7 +20,8 @@
|
|||||||
let usageInterval: any;
|
let usageInterval: any;
|
||||||
let loading = {
|
let loading = {
|
||||||
usage: false,
|
usage: false,
|
||||||
cleanup: false
|
cleanup: false,
|
||||||
|
restart: false
|
||||||
};
|
};
|
||||||
import { addToast, appSession } from '$lib/store';
|
import { addToast, appSession } from '$lib/store';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
@@ -33,6 +34,25 @@
|
|||||||
usage = data.usage;
|
usage = data.usage;
|
||||||
loading.usage = false;
|
loading.usage = false;
|
||||||
}
|
}
|
||||||
|
async function restartCoolify() {
|
||||||
|
const sure = confirm(
|
||||||
|
'Are you sure you would like to restart Coolify? Currently running deployments will be stopped and restarted.'
|
||||||
|
);
|
||||||
|
if (sure) {
|
||||||
|
loading.restart = true;
|
||||||
|
try {
|
||||||
|
await post(`/internal/restart`, {});
|
||||||
|
addToast({
|
||||||
|
type: 'success',
|
||||||
|
message: 'Coolify restarted successfully. It will take a moment.'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return errorNotification(error);
|
||||||
|
} finally {
|
||||||
|
loading.restart = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
clearInterval(usageInterval);
|
clearInterval(usageInterval);
|
||||||
});
|
});
|
||||||
@@ -67,51 +87,56 @@
|
|||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h1 class="title text-4xl">Hardware Details</h1>
|
<h1 class="title text-4xl">Hardware Details</h1>
|
||||||
{#if $appSession.teamId === '0'}
|
<div class="flex space-x-4">
|
||||||
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
|
{#if $appSession.teamId === '0'}
|
||||||
>Cleanup Storage</button
|
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
|
||||||
>
|
>Cleanup Storage</button
|
||||||
{/if}
|
>
|
||||||
|
<button
|
||||||
|
on:click={restartCoolify}
|
||||||
|
class:loading={loading.restart}
|
||||||
|
class="btn btn-sm bg-red-600 hover:bg-red-500">Restart Coolify</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="grid grid-flow-col gap-4 grid-rows-3 lg:grid-rows-1">
|
<div class="grid grid-flow-col gap-4 grid-rows-3 lg:grid-rows-1">
|
||||||
<div
|
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
|
||||||
class="stats stats-vertical lg:stats-horizontal shadow w-full mb-5 bg-coolgray-200 rounded"
|
<div class="font-bold flex lg:justify-center">Memory</div>
|
||||||
>
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Total Memory</div>
|
<div class="stat-title">Total</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
|
{(usage?.memory.totalMemMb).toFixed(0)}<span class="text-sm">MB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Used Memory</div>
|
<div class="stat-title">Used</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
|
{(usage?.memory.usedMemMb).toFixed(0)}<span class="text-sm">MB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Free Memory</div>
|
<div class="stat-title">Free</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
|
{usage?.memory.freeMemPercentage}<span class="text-sm">%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
|
||||||
class="stats stats-vertical lg:stats-horizontal shadow w-full mb-5 bg-coolgray-200 rounded"
|
<div class="font-bold flex lg:justify-center">CPU</div>
|
||||||
>
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Total CPUs</div>
|
<div class="stat-title">Total</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.cpu.count}
|
{usage?.cpu.count}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">CPU Usage</div>
|
<div class="stat-title">Usage</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.cpu.usage}<span class="text-sm">%</span>
|
{usage?.cpu.usage}<span class="text-sm">%</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,26 +147,24 @@
|
|||||||
<div class="stat-value text-2xl">{usage?.cpu.load}</div>
|
<div class="stat-value text-2xl">{usage?.cpu.load}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="stats stats-vertical lg:stats-horizontal w-full mb-5 bg-transparent rounded">
|
||||||
<div
|
<div class="font-bold flex lg:justify-center">Disk</div>
|
||||||
class="stats stats-vertical lg:stats-horizontal shadow w-full mb-5 bg-coolgray-200 rounded"
|
|
||||||
>
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Total Disk</div>
|
<div class="stat-title">Total</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.disk.totalGb}<span class="text-sm">GB</span>
|
{usage?.disk.totalGb}<span class="text-sm">GB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Used Disk</div>
|
<div class="stat-title">Used</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.disk.usedGb}<span class="text-sm">GB</span>
|
{usage?.disk.usedGb}<span class="text-sm">GB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Free Disk</div>
|
<div class="stat-title">Free</div>
|
||||||
<div class="stat-value text-2xl">
|
<div class="stat-value text-2xl">
|
||||||
{usage?.disk.freePercentage}<span class="text-sm">%</span>
|
{usage?.disk.freePercentage}<span class="text-sm">%</span>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -86,7 +86,7 @@
|
|||||||
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
||||||
{#each applications as application}
|
{#each applications as application}
|
||||||
<a class="no-underline mb-5" href={`/applications/${application.id}`}>
|
<a class="no-underline mb-5" href={`/applications/${application.id}`}>
|
||||||
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-coolgray-300 indicator">
|
<div class="w-full rounded p-5 bg-coolgray-100 hover:bg-coolgray-300 indicator">
|
||||||
{#await getStatus(application)}
|
{#await getStatus(application)}
|
||||||
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
||||||
{:then status}
|
{:then status}
|
||||||
@@ -99,15 +99,18 @@
|
|||||||
<div class="w-full flex flex-row">
|
<div class="w-full flex flex-row">
|
||||||
<ApplicationsIcons {application} isAbsolute={false} />
|
<ApplicationsIcons {application} isAbsolute={false} />
|
||||||
<div class="w-full flex flex-col ml-5">
|
<div class="w-full flex flex-col ml-5">
|
||||||
<span>
|
<h1 class="font-bold text-lg">
|
||||||
Application
|
{application.name}
|
||||||
{#if application.settings.isBot}
|
{#if application.settings.isBot}
|
||||||
| BOT
|
| BOT
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</h1>
|
||||||
<h1 class="font-bold text-lg">{application.name}</h1>
|
{#if application?.fqdn}
|
||||||
<div class="divider" />
|
<h2>{application?.fqdn}</h2>
|
||||||
<div class="flex justify-end space-x-2">
|
{:else if !application.settings.isBot && !application?.fqdn}
|
||||||
|
<h2 class="text-red-500">Not configured</h2>
|
||||||
|
{/if}
|
||||||
|
<div class="flex justify-end space-x-2 pt-2">
|
||||||
{#if application.fqdn}
|
{#if application.fqdn}
|
||||||
<a href={application.fqdn} target="_blank" class="icons">
|
<a href={application.fqdn} target="_blank" class="icons">
|
||||||
<svg
|
<svg
|
||||||
@@ -157,12 +160,14 @@
|
|||||||
</a>
|
</a>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if services.length > 0}
|
||||||
<h1 class="title text-4xl mt-10">Services</h1>
|
<h1 class="title text-4xl mt-10">Services</h1>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
||||||
{#each services as service}
|
{#each services as service}
|
||||||
<a class="no-underline mb-5" href={`/services/${service.id}`}>
|
<a class="no-underline mb-5" href={`/services/${service.id}`}>
|
||||||
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-coolgray-300 indicator">
|
<div class="w-full rounded p-5 bg-coolgray-100 hover:bg-coolgray-300 indicator">
|
||||||
{#await getStatus(service)}
|
{#await getStatus(service)}
|
||||||
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
||||||
{:then status}
|
{:then status}
|
||||||
@@ -175,10 +180,13 @@
|
|||||||
<div class="w-full flex flex-row">
|
<div class="w-full flex flex-row">
|
||||||
<ServiceIcons type={service.type} isAbsolute={false} />
|
<ServiceIcons type={service.type} isAbsolute={false} />
|
||||||
<div class="w-full flex flex-col ml-5">
|
<div class="w-full flex flex-col ml-5">
|
||||||
<span> Service </span>
|
|
||||||
<h1 class="font-bold text-lg">{service.name}</h1>
|
<h1 class="font-bold text-lg">{service.name}</h1>
|
||||||
<div class="divider" />
|
{#if service?.fqdn}
|
||||||
<div class="flex justify-end space-x-2">
|
<h2>{service?.fqdn}</h2>
|
||||||
|
{:else}
|
||||||
|
<h2 class="text-red-500">Not configured</h2>
|
||||||
|
{/if}
|
||||||
|
<div class="flex justify-end space-x-2 pt-2">
|
||||||
{#if service.fqdn}
|
{#if service.fqdn}
|
||||||
<a href={service.fqdn} target="_blank" class="icons">
|
<a href={service.fqdn} target="_blank" class="icons">
|
||||||
<svg
|
<svg
|
||||||
@@ -205,12 +213,14 @@
|
|||||||
</a>
|
</a>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if databases.length > 0}
|
||||||
<h1 class="title text-4xl mt-10">Databases</h1>
|
<h1 class="title text-4xl mt-10">Databases</h1>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
<div class="grid grid-col gap-4 auto-cols-max grid-cols-1 lg:grid-cols-3">
|
||||||
{#each databases as database}
|
{#each databases as database}
|
||||||
<a class="no-underline mb-5" href={`/databases/${database.id}`}>
|
<a class="no-underline mb-5" href={`/databases/${database.id}`}>
|
||||||
<div class="w-full rounded p-5 bg-coolgray-200 hover:bg-coolgray-300 indicator">
|
<div class="w-full rounded p-5 bg-coolgray-100 hover:bg-coolgray-300 indicator">
|
||||||
{#await getStatus(database)}
|
{#await getStatus(database)}
|
||||||
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
<span class="indicator-item badge bg-yellow-500 badge-xs" />
|
||||||
{:then status}
|
{:then status}
|
||||||
@@ -220,12 +230,10 @@
|
|||||||
<span class="indicator-item badge bg-error badge-xs" />
|
<span class="indicator-item badge bg-error badge-xs" />
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
<div class="w-full flex flex-row">
|
<div class="w-full flex flex-row pt-2">
|
||||||
<DatabaseIcons type={database.type} isAbsolute={false} />
|
<DatabaseIcons type={database.type} isAbsolute={false} />
|
||||||
<div class="w-full flex flex-col ml-5">
|
<div class="w-full flex flex-col ml-5">
|
||||||
<span> Service </span>
|
|
||||||
<h1 class="font-bold text-lg">{database.name}</h1>
|
<h1 class="font-bold text-lg">{database.name}</h1>
|
||||||
<div class="divider" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user