feat: single source of truth for app version from health check

Read version from the backend /api/health endpoint instead of
hardcoding from package.json at build time. Add About section
in Settings with link to the DZone design article.
This commit is contained in:
Pier-Jean Malandrino 2026-04-09 15:51:59 +02:00
parent f487b752dc
commit abf3325923
3 changed files with 78 additions and 3 deletions

View file

@ -7,6 +7,7 @@ type DeploymentMode = 'self-hosted' | 'huggingface'
interface HealthResponse {
status: string
version?: string
engine: ConversionEngine
deploymentMode?: DeploymentMode
maxPageCount?: number
@ -41,6 +42,7 @@ export const useFeatureFlagStore = defineStore('feature-flags', () => {
const deploymentMode = ref<DeploymentMode | null>(null)
const maxPageCount = ref<number>(0)
const maxFileSizeMb = ref<number>(0)
const appVersion = ref<string>(__APP_VERSION__)
const loaded = ref(false)
const error = ref<string | null>(null)
@ -62,6 +64,7 @@ export const useFeatureFlagStore = defineStore('feature-flags', () => {
deploymentMode.value = data.deploymentMode ?? 'self-hosted'
maxPageCount.value = data.maxPageCount ?? 0
maxFileSizeMb.value = data.maxFileSizeMb ?? 0
if (data.version) appVersion.value = data.version
loaded.value = true
error.value = null
} catch (e) {
@ -70,5 +73,15 @@ export const useFeatureFlagStore = defineStore('feature-flags', () => {
}
}
return { engine, deploymentMode, maxPageCount, maxFileSizeMb, loaded, error, isEnabled, load }
return {
engine,
deploymentMode,
maxPageCount,
maxFileSizeMb,
appVersion,
loaded,
error,
isEnabled,
load,
}
})

View file

@ -36,15 +36,43 @@
<label class="setting-label">{{ t('settings.version') }}</label>
<span class="setting-value">{{ version }}</span>
</div>
<div class="setting-group">
<label class="setting-label">{{ t('settings.about') }}</label>
<a
href="https://dzone.com/articles/designing-docling-studio"
target="_blank"
rel="noopener noreferrer"
class="about-link"
>
<svg viewBox="0 0 20 20" fill="currentColor" class="about-link-icon">
<path
d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z"
/>
</svg>
{{ t('settings.designArticle') }}
<svg viewBox="0 0 20 20" fill="currentColor" class="about-link-external">
<path
d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"
/>
<path
d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"
/>
</svg>
</a>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSettingsStore } from '../store'
import { useFeatureFlagStore } from '../../feature-flags/store'
import { useI18n } from '../../../shared/i18n'
const version = __APP_VERSION__
const store = useSettingsStore()
const featureStore = useFeatureFlagStore()
const version = computed(() => featureStore.appVersion)
const { t } = useI18n()
</script>
@ -118,4 +146,35 @@ const { t } = useI18n()
color: var(--text);
font-family: 'IBM Plex Mono', monospace;
}
.about-link {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--text-secondary);
text-decoration: none;
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-elevated);
transition: all var(--transition);
width: fit-content;
}
.about-link:hover {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-muted);
}
.about-link-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.about-link-external {
width: 12px;
height: 12px;
flex-shrink: 0;
opacity: 0.4;
}
</style>

View file

@ -97,10 +97,13 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { useI18n } from '../i18n'
import { useFeatureFlagStore } from '../../features/feature-flags/store'
const version = __APP_VERSION__
const featureStore = useFeatureFlagStore()
const version = computed(() => featureStore.appVersion)
const route = useRoute()
const { t } = useI18n()