feat(frontend) : add database info section and form field
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
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:
@@ -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 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user