feat(frontend) : add database info section and form field
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-04-08 16:07:51 +02:00
parent 20d6dcea45
commit 95c90a258f

View File

@@ -4,8 +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 type { EnvironmentHealth, DatabaseInfo } from '~/services/dto/dashboard'
import { getEnvironmentHealth } from '~/services/dashboard' import { getEnvironmentHealth, getDatabaseInfo } from '~/services/dashboard'
import type { LogOutput } from '~/services/dto/logs' import type { LogOutput } from '~/services/dto/logs'
import { getDockerLogs, getSymfonyLog } from '~/services/logs' import { getDockerLogs, getSymfonyLog } from '~/services/logs'
@@ -31,6 +31,9 @@ const deployResult = ref<DeployResult | null>(null)
const healthByEnvId = ref<Record<number, EnvironmentHealth>>({}) const healthByEnvId = ref<Record<number, EnvironmentHealth>>({})
const loadingHealth = ref(false) const loadingHealth = ref(false)
// Database info per env
const dbInfoByEnvId = ref<Record<number, DatabaseInfo>>({})
// Log modals // Log modals
const showLogModal = ref(false) const showLogModal = ref(false)
const logContent = ref('') const logContent = ref('')
@@ -54,6 +57,7 @@ const envForm = ref<EnvironmentWrite>({
deployScriptPath: '', deployScriptPath: '',
maintenanceFilePath: '', maintenanceFilePath: '',
appUrl: '', appUrl: '',
databaseName: '',
logFiles: [], logFiles: [],
}) })
const isSubmittingEnv = ref(false) const isSubmittingEnv = ref(false)
@@ -66,6 +70,7 @@ async function loadApplication() {
loading.value = false loading.value = false
} }
loadHealthData() loadHealthData()
loadDatabaseData()
} }
// Application edit // Application edit
@@ -103,7 +108,7 @@ async function handleDeleteApp() {
// Environment CRUD // Environment CRUD
function openCreateEnvModal() { function openCreateEnvModal() {
editingEnvId.value = null editingEnvId.value = null
envForm.value = { name: '', containerName: '', deployScriptPath: '', maintenanceFilePath: '', appUrl: '', logFiles: [] } envForm.value = { name: '', containerName: '', deployScriptPath: '', maintenanceFilePath: '', appUrl: '', databaseName: '', logFiles: [] }
showEnvModal.value = true showEnvModal.value = true
} }
@@ -115,6 +120,7 @@ function openEditEnvModal(env: Environment) {
deployScriptPath: env.deployScriptPath, deployScriptPath: env.deployScriptPath,
maintenanceFilePath: env.maintenanceFilePath, maintenanceFilePath: env.maintenanceFilePath,
appUrl: env.appUrl ?? '', appUrl: env.appUrl ?? '',
databaseName: env.databaseName ?? '',
logFiles: env.logFiles.map(lf => ({ label: lf.label, path: lf.path })), logFiles: env.logFiles.map(lf => ({ label: lf.label, path: lf.path })),
} }
showEnvModal.value = true showEnvModal.value = true
@@ -215,6 +221,20 @@ async function loadHealthData() {
} }
} }
async function loadDatabaseData() {
if (!application.value?.environments?.length) return
const promises = application.value.environments.map(async (env) => {
if (!env.databaseName) return
try {
const info = await getDatabaseInfo(env.id!)
dbInfoByEnvId.value[env.id!] = info
} catch {
// silently ignore — no database configured or unreachable
}
})
await Promise.all(promises)
}
function formatUptime(startedAt: string): string { function formatUptime(startedAt: string): string {
if (!startedAt) return '-' if (!startedAt) return '-'
const start = new Date(startedAt) const start = new Date(startedAt)
@@ -471,6 +491,50 @@ onMounted(loadApplication)
</div> </div>
</div> </div>
</div> </div>
<!-- Database info -->
<div v-if="dbInfoByEnvId[env.id!]" class="mt-4 border-t border-neutral-200 py-3">
<p class="text-sm font-bold uppercase tracking-wider mb-2">{{ t('environments.database.title') }}</p>
<div class="grid grid-cols-2 sm:grid-cols-6 gap-3">
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.status') }}</p>
<span
class="inline-block rounded-full px-2.5 py-0.5 text-xs font-semibold mt-1"
:class="dbInfoByEnvId[env.id!].connected
? 'bg-green-100 text-green-700'
: 'bg-red-100 text-red-700'"
>
{{ dbInfoByEnvId[env.id!].connected
? t('environments.database.connected')
: t('environments.database.unreachable') }}
</span>
</div>
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.name') }}</p>
<p class="text-sm font-mono text-neutral-800 mt-1">{{ dbInfoByEnvId[env.id!].name }}</p>
</div>
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.size') }}</p>
<p class="text-sm text-neutral-800 mt-1">{{ dbInfoByEnvId[env.id!].size || '-' }}</p>
</div>
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.tableCount') }}</p>
<p class="text-sm text-neutral-800 mt-1">{{ dbInfoByEnvId[env.id!].tableCount }}</p>
</div>
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.activeConnections') }}</p>
<p class="text-sm text-neutral-800 mt-1">{{ dbInfoByEnvId[env.id!].activeConnections }}</p>
</div>
<div>
<p class="text-xs text-neutral-400">{{ t('environments.database.cacheHitRatio') }}</p>
<p class="text-sm text-neutral-800 mt-1">{{ dbInfoByEnvId[env.id!].cacheHitRatio }}%</p>
</div>
</div>
<p v-if="dbInfoByEnvId[env.id!].largestTable && dbInfoByEnvId[env.id!].largestTable !== '-'" class="text-xs text-neutral-500 mt-2">
{{ t('environments.database.largestTable') }} : <span class="font-mono">{{ dbInfoByEnvId[env.id!].largestTable }}</span>
</p>
</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')"
@@ -577,6 +641,12 @@ onMounted(loadApplication)
:label="t('environments.form.appUrl')" :label="t('environments.form.appUrl')"
groupClass="mt-0" groupClass="mt-0"
/> />
<MalioInputText
v-model="envForm.databaseName"
:label="t('environments.database.formLabel')"
:hint="t('environments.database.formHint')"
groupClass="mt-0"
/>
</div> </div>
<!-- Log files --> <!-- Log files -->