diff --git a/frontend/pages/applications/[slug].vue b/frontend/pages/applications/[slug].vue index 5192fae..6d1cdc4 100644 --- a/frontend/pages/applications/[slug].vue +++ b/frontend/pages/applications/[slug].vue @@ -4,6 +4,8 @@ import { getApplication, updateApplication, deleteApplication } from '~/services import { createEnvironment, updateEnvironment, deleteEnvironment, toggleMaintenance } from '~/services/environments' import type { DeployResult } from '~/services/dto/deploy' import { getAvailableTags, deploy } from '~/services/deploy' +import type { EnvironmentHealth } from '~/services/dto/dashboard' +import { getEnvironmentHealth } from '~/services/dashboard' const { t } = useI18n() const route = useRoute() @@ -23,6 +25,10 @@ const loadingTags = ref(false) const isDeploying = ref(false) const deployResult = ref(null) +// Health data per env +const healthByEnvId = ref>({}) +const loadingHealth = ref(false) + // App edit modal const showAppModal = ref(false) const editForm = ref({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' }) @@ -48,6 +54,7 @@ async function loadApplication() { } finally { loading.value = false } + loadHealthData() } // Application edit @@ -179,6 +186,46 @@ function closeDeployModal() { deployResult.value = null } +async function loadHealthData() { + if (!application.value?.environments?.length) return + loadingHealth.value = true + try { + const promises = application.value.environments.map(async (env) => { + try { + const health = await getEnvironmentHealth(env.id!) + healthByEnvId.value[env.id!] = health + } catch { + // silently ignore individual env health failures + } + }) + await Promise.all(promises) + } finally { + loadingHealth.value = false + } +} + +function formatUptime(startedAt: string): string { + if (!startedAt) return '-' + const start = new Date(startedAt) + const now = new Date() + const diffMs = now.getTime() - start.getTime() + const days = Math.floor(diffMs / 86400000) + const hours = Math.floor((diffMs % 86400000) / 3600000) + const minutes = Math.floor((diffMs % 3600000) / 60000) + if (days > 0) return `${days}j ${hours}h` + if (hours > 0) return `${hours}h ${minutes}m` + return `${minutes}m` +} + +function statusClass(status: string): string { + switch (status) { + case 'running': return 'bg-green-100 text-green-700' + case 'exited': return 'bg-red-100 text-red-700' + case 'restarting': return 'bg-orange-100 text-orange-700' + default: return 'bg-neutral-100 text-neutral-500' + } +} + const envModalTitle = computed(() => editingEnvId.value ? t('environments.editButton') : t('environments.addButton') ) @@ -313,6 +360,41 @@ onMounted(loadApplication) {{ lf.path }} + + +
+

{{ t('environments.health.title') }}

+
+
+

{{ t('environments.health.status') }}

+ + {{ t(`dashboard.status.${healthByEnvId[env.id!].status}`) }} + +
+
+

{{ t('environments.health.version') }}

+

{{ healthByEnvId[env.id!].version || '-' }}

+
+
+

{{ t('environments.health.uptime') }}

+

{{ formatUptime(healthByEnvId[env.id!].startedAt) }}

+
+
+

{{ t('environments.health.cpu') }}

+

{{ healthByEnvId[env.id!].cpuPercent }}%

+
+
+

{{ t('environments.health.memory') }}

+

+ {{ healthByEnvId[env.id!].memoryUsage }} / {{ healthByEnvId[env.id!].memoryLimit }} + ({{ healthByEnvId[env.id!].memoryPercent }}%) +

+
+
+