feat : add health metrics block on environment detail
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ import { getApplication, updateApplication, deleteApplication } from '~/services
|
|||||||
import { createEnvironment, updateEnvironment, deleteEnvironment, toggleMaintenance } from '~/services/environments'
|
import { createEnvironment, updateEnvironment, deleteEnvironment, toggleMaintenance } from '~/services/environments'
|
||||||
import type { DeployResult } from '~/services/dto/deploy'
|
import type { DeployResult } from '~/services/dto/deploy'
|
||||||
import { getAvailableTags, deploy } from '~/services/deploy'
|
import { getAvailableTags, deploy } from '~/services/deploy'
|
||||||
|
import type { EnvironmentHealth } from '~/services/dto/dashboard'
|
||||||
|
import { getEnvironmentHealth } from '~/services/dashboard'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -23,6 +25,10 @@ const loadingTags = ref(false)
|
|||||||
const isDeploying = ref(false)
|
const isDeploying = ref(false)
|
||||||
const deployResult = ref<DeployResult | null>(null)
|
const deployResult = ref<DeployResult | null>(null)
|
||||||
|
|
||||||
|
// Health data per env
|
||||||
|
const healthByEnvId = ref<Record<number, EnvironmentHealth>>({})
|
||||||
|
const loadingHealth = ref(false)
|
||||||
|
|
||||||
// App edit modal
|
// App edit modal
|
||||||
const showAppModal = ref(false)
|
const showAppModal = ref(false)
|
||||||
const editForm = ref<ApplicationWrite>({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' })
|
const editForm = ref<ApplicationWrite>({ name: '', slug: '', registryImage: '', description: '', giteaUrl: '' })
|
||||||
@@ -48,6 +54,7 @@ async function loadApplication() {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
loadHealthData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application edit
|
// Application edit
|
||||||
@@ -179,6 +186,46 @@ function closeDeployModal() {
|
|||||||
deployResult.value = null
|
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(() =>
|
const envModalTitle = computed(() =>
|
||||||
editingEnvId.value ? t('environments.editButton') : t('environments.addButton')
|
editingEnvId.value ? t('environments.editButton') : t('environments.addButton')
|
||||||
)
|
)
|
||||||
@@ -313,6 +360,41 @@ onMounted(loadApplication)
|
|||||||
<span class="font-mono text-neutral-400">{{ lf.path }}</span>
|
<span class="font-mono text-neutral-400">{{ lf.path }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Health metrics -->
|
||||||
|
<div v-if="healthByEnvId[env.id!]" class="mt-4 border-t border-neutral-200 pt-3">
|
||||||
|
<p class="text-xs font-semibold uppercase tracking-wider text-neutral-400 mb-3">{{ t('environments.health.title') }}</p>
|
||||||
|
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-neutral-400">{{ t('environments.health.status') }}</p>
|
||||||
|
<span
|
||||||
|
class="inline-block rounded-full px-2.5 py-0.5 text-xs font-semibold mt-1"
|
||||||
|
:class="statusClass(healthByEnvId[env.id!].status)"
|
||||||
|
>
|
||||||
|
{{ t(`dashboard.status.${healthByEnvId[env.id!].status}`) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-neutral-400">{{ t('environments.health.version') }}</p>
|
||||||
|
<p class="text-sm font-mono text-neutral-800 mt-1">{{ healthByEnvId[env.id!].version || '-' }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-neutral-400">{{ t('environments.health.uptime') }}</p>
|
||||||
|
<p class="text-sm text-neutral-800 mt-1">{{ formatUptime(healthByEnvId[env.id!].startedAt) }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-neutral-400">{{ t('environments.health.cpu') }}</p>
|
||||||
|
<p class="text-sm text-neutral-800 mt-1">{{ healthByEnvId[env.id!].cpuPercent }}%</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2">
|
||||||
|
<p class="text-xs text-neutral-400">{{ t('environments.health.memory') }}</p>
|
||||||
|
<p class="text-sm text-neutral-800 mt-1">
|
||||||
|
{{ healthByEnvId[env.id!].memoryUsage }} / {{ healthByEnvId[env.id!].memoryLimit }}
|
||||||
|
<span class="text-neutral-400">({{ healthByEnvId[env.id!].memoryPercent }}%)</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex justify-center gap-4 mt-4">
|
<div class="flex justify-center gap-4 mt-4">
|
||||||
<MalioButton
|
<MalioButton
|
||||||
:label="t('environments.editButton')"
|
:label="t('environments.editButton')"
|
||||||
|
|||||||
Reference in New Issue
Block a user