feat: implement version display with commit hash and date

This commit is contained in:
2026-05-09 14:05:25 +03:00
parent debf1b165f
commit 1e7587e11b
11 changed files with 199 additions and 5 deletions

View File

@@ -21,10 +21,12 @@
</style>
<script setup lang="ts">
import { watch } from 'vue'
import { watch, onMounted } from 'vue'
import { useSettingsStore } from '@/stores/settings'
import { useVersionStore } from '@/stores/version'
const settings = useSettingsStore()
const versionStore = useVersionStore()
watch(() => settings.siteDescription, (desc) => {
let meta = document.querySelector('meta[name="description"]')
@@ -35,4 +37,7 @@ watch(() => settings.siteDescription, (desc) => {
}
meta.setAttribute('content', desc || '')
}, { immediate: true })
onMounted(() => {
versionStore.fetchVersion()
})
</script>

View File

@@ -167,6 +167,16 @@
{{ userInitials }}
</div>
</div>
<!-- Версия сборки (всегда внизу) -->
<div v-if="!sidebarCollapsed" class="px-4 py-3 border-t border-gray-200 text-xs text-gray-500">
{{ versionStore.getFormattedVersion(t) }}
</div>
<div v-else class="p-2 border-t border-gray-200 flex justify-center">
<div class="text-xs text-gray-500 font-mono" :title="versionStore.getFormattedVersion(t)">
{{ versionStore.version?.commitHash?.slice(0, 6) }}
</div>
</div>
</div>
</aside>
@@ -256,9 +266,11 @@ import { useSettingsStore } from '@/stores/settings'
import { useUserStore } from '@/stores/user'
import { useI18n } from 'vue-i18n'
import { useNotification } from '@/composables/useNotification'
import { useVersionStore } from '@/stores/version'
const { notification, showNotification } = useNotification()
const settings = useSettingsStore()
const versionStore = useVersionStore()
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()

View File

@@ -46,7 +46,9 @@
"deleteConfirmation": "Are you sure you want to delete this item? This action cannot be undone.",
"operationSuccess": "Operation completed successfully",
"operationFailed": "Operation failed",
"networkError": "Network error"
"networkError": "Network error",
"version": "Version",
"versionFrom": "from"
},
"dashboard": {
"totalUsers": "Total Users",

View File

@@ -46,7 +46,9 @@
"deleteConfirmation": "Вы уверены, что хотите удалить этот элемент? Это действие необратимо.",
"operationSuccess": "Операция выполнена успешно",
"operationFailed": "Операция не удалась",
"networkError": "Ошибка сети"
"networkError": "Ошибка сети",
"version": "Версия",
"versionFrom": "от"
},
"dashboard": {
"totalUsers": "Всего пользователей",

View File

@@ -0,0 +1,55 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export interface BuildVersion {
version: string
commitHash: string
buildTime: string // ISO строка, например "2025-04-03T12:34:56Z"
}
export const useVersionStore = defineStore('version', () => {
const version = ref<BuildVersion | null>(null)
const loading = ref(false)
const error = ref<string | null>(null)
async function fetchVersion() {
if (version.value) return
loading.value = true
try {
const res = await fetch('/api/build-info')
if (!res.ok) throw new Error('Failed to fetch version')
const data = await res.json()
version.value = {
version: data.version || '0.0.0',
commitHash: data.commitHash || 'unknown',
buildTime: data.buildTime || ''
}
} catch (err: any) {
console.error(err)
error.value = err.message
version.value = { version: 'dev', commitHash: 'unknown', buildTime: '' }
} finally {
loading.value = false
}
}
// Отформатированная дата сборки (только дата, без времени)
const buildDateFormatted = computed(() => {
if (!version.value?.buildTime) return ''
const date = new Date(version.value.buildTime)
if (isNaN(date.getTime())) return ''
// Формат YYYY-MM-DD (универсальный, без локализации)
return date.toISOString().split('T')[0]
})
// Полная строка версии: "Версия: 1.2.3 (build abc1234 от 2025-04-03)"
// Принимает функцию перевода для слова "от"/"from"
const getFormattedVersion = (t: (key: string) => string) => {
if (!version.value) return t('common.version') + ': ...'
const { version: ver, commitHash } = version.value
const datePart = buildDateFormatted.value ? ` ${t('common.versionFrom')} ${buildDateFormatted.value}` : ''
return `${t('common.version')}: ${ver} (build ${commitHash}${datePart})`
}
return { version, loading, error, fetchVersion, buildDateFormatted, getFormattedVersion }
})

View File

@@ -96,6 +96,12 @@
</div>
</transition>
</div>
<!-- Блок версии внизу -->
<div class="mt-6 text-center text-xs text-gray-500">
{{ versionStore.getFormattedVersion(t) }}
</div>
</div>
</div>
</template>
@@ -105,12 +111,14 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useSettingsStore } from '@/stores/settings'
import { useUserStore } from '@/stores/user'
import { useVersionStore } from '@/stores/version'
import { useI18n } from 'vue-i18n'
const settings = useSettingsStore()
const userStore = useUserStore()
const router = useRouter()
const { t, locale } = useI18n()
const versionStore = useVersionStore()
const form = ref({ login: '', password: '' })
const loading = ref(false)
const error = ref('')

View File

@@ -35,6 +35,12 @@
{{ t('register.alreadyHaveAccount') }} <router-link to="/login" class="text-primary-600">{{ t('login.signin') }}</router-link>
</p>
</div>
<!-- Блок версии внизу -->
<div class="mt-6 text-center text-xs text-gray-500">
{{ versionStore.getFormattedVersion(t) }}
</div>
</div>
</div>
</template>
@@ -42,7 +48,9 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useVersionStore } from '@/stores/version'
const { t } = useI18n()
const versionStore = useVersionStore()
const form = ref({ login: '', email: '', password: '' })
const loading = ref(false)
const error = ref('')