mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-13 19:16:40 +07:00
feat: показывать версию приложения в настройках, версия 1.1.0
Версии бэкенда и фронтенда были не синхронизированы и нигде не отображались в интерфейсе — непонятно было, обновилось ли приложение после пересборки. Теперь единая версия 1.1.0 приходит с бэкенда через /api/health и мелким текстом выводится внизу страницы настроек.
This commit is contained in:
@@ -4,9 +4,10 @@ from functools import lru_cache
|
|||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
# Single source of truth for the running app's version — surfaced in the API
|
# Single source of truth for the running app's version — surfaced in the API
|
||||||
# title and embedded in exported backups so an old file can be told apart
|
# title, /api/health (which the frontend reads to show it in Settings), and
|
||||||
# from a current one.
|
# embedded in exported backups so an old file can be told apart from a
|
||||||
APP_VERSION = "0.20.0"
|
# current one.
|
||||||
|
APP_VERSION = "1.1.0"
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
|||||||
@@ -88,4 +88,7 @@ app.include_router(crypto.router, prefix="/api")
|
|||||||
|
|
||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
async def health() -> dict[str, str]:
|
async def health() -> dict[str, str]:
|
||||||
return {"status": "ok"}
|
# version rides along so the frontend's Settings page can show which
|
||||||
|
# release is actually running without a separate authenticated endpoint —
|
||||||
|
# this route is already auth_basic-exempt for Docker's HEALTHCHECK.
|
||||||
|
return {"status": "ok", "version": APP_VERSION}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "aurum-frontend",
|
"name": "aurum-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.26.0",
|
"version": "1.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
6
frontend/src/api/health.ts
Normal file
6
frontend/src/api/health.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { api } from "@/api/client";
|
||||||
|
import type { HealthStatus } from "@/types";
|
||||||
|
|
||||||
|
export function fetchHealth() {
|
||||||
|
return api.get<HealthStatus>("/health");
|
||||||
|
}
|
||||||
8
frontend/src/hooks/useHealth.ts
Normal file
8
frontend/src/hooks/useHealth.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { fetchHealth } from "@/api/health";
|
||||||
|
|
||||||
|
// The running version only changes on a deploy (a full page reload), so
|
||||||
|
// there's no need to ever refetch within a session.
|
||||||
|
export function useHealth() {
|
||||||
|
return useQuery({ queryKey: ["health"], queryFn: fetchHealth, staleTime: Infinity });
|
||||||
|
}
|
||||||
@@ -341,6 +341,7 @@ const ru = {
|
|||||||
"Восстановить данные из «{{filename}}»?\n\nЭто ПОЛНОСТЬЮ заменит все текущие счета, категории, транзакции и активы данными из файла. Отменить это действие нельзя — если хотите сохранить текущие данные, сначала сделайте экспорт.",
|
"Восстановить данные из «{{filename}}»?\n\nЭто ПОЛНОСТЬЮ заменит все текущие счета, категории, транзакции и активы данными из файла. Отменить это действие нельзя — если хотите сохранить текущие данные, сначала сделайте экспорт.",
|
||||||
"backup.footerWarning":
|
"backup.footerWarning":
|
||||||
"Восстановление полностью заменяет текущие данные содержимым файла — это нельзя отменить.",
|
"Восстановление полностью заменяет текущие данные содержимым файла — это нельзя отменить.",
|
||||||
|
"settings.version": "Aurum v{{version}}",
|
||||||
|
|
||||||
"insights.negativeCashFlow": "Расходы превышают доходы {{months}} мес. подряд (по последним завершённым месяцам)",
|
"insights.negativeCashFlow": "Расходы превышают доходы {{months}} мес. подряд (по последним завершённым месяцам)",
|
||||||
"insights.netWorthDecline": "Капитал снижается {{months}} мес. подряд",
|
"insights.netWorthDecline": "Капитал снижается {{months}} мес. подряд",
|
||||||
@@ -823,6 +824,7 @@ const en: Record<keyof typeof ru, string> = {
|
|||||||
"Restore data from “{{filename}}”?\n\nThis will COMPLETELY replace all current accounts, categories, transactions, and assets with the data from the file. This action cannot be undone — if you want to keep your current data, export it first.",
|
"Restore data from “{{filename}}”?\n\nThis will COMPLETELY replace all current accounts, categories, transactions, and assets with the data from the file. This action cannot be undone — if you want to keep your current data, export it first.",
|
||||||
"backup.footerWarning":
|
"backup.footerWarning":
|
||||||
"Restoring completely replaces your current data with the file's contents — this cannot be undone.",
|
"Restoring completely replaces your current data with the file's contents — this cannot be undone.",
|
||||||
|
"settings.version": "Aurum v{{version}}",
|
||||||
|
|
||||||
"insights.negativeCashFlow": "Expenses have exceeded income for {{months}} months in a row (based on the last completed months)",
|
"insights.negativeCashFlow": "Expenses have exceeded income for {{months}} months in a row (based on the last completed months)",
|
||||||
"insights.netWorthDecline": "Net worth has been declining for {{months}} months in a row",
|
"insights.netWorthDecline": "Net worth has been declining for {{months}} months in a row",
|
||||||
|
|||||||
@@ -2,14 +2,26 @@ import { AlertThresholdsCard } from "@/components/settings/AlertThresholdsCard";
|
|||||||
import { BackupCard } from "@/components/settings/BackupCard";
|
import { BackupCard } from "@/components/settings/BackupCard";
|
||||||
import { CurrencyCard } from "@/components/settings/CurrencyCard";
|
import { CurrencyCard } from "@/components/settings/CurrencyCard";
|
||||||
import { PreferencesCard } from "@/components/settings/PreferencesCard";
|
import { PreferencesCard } from "@/components/settings/PreferencesCard";
|
||||||
|
import { useHealth } from "@/hooks/useHealth";
|
||||||
|
import { t } from "@/lib/i18n";
|
||||||
|
|
||||||
export function SettingsPage() {
|
export function SettingsPage() {
|
||||||
|
// Purely informational — if /api/health hasn't answered yet (or is
|
||||||
|
// unreachable), just show nothing rather than a loading/error state for
|
||||||
|
// one line of fine print.
|
||||||
|
const { data: health } = useHealth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<PreferencesCard />
|
<PreferencesCard />
|
||||||
<CurrencyCard />
|
<CurrencyCard />
|
||||||
<AlertThresholdsCard />
|
<AlertThresholdsCard />
|
||||||
<BackupCard />
|
<BackupCard />
|
||||||
|
{health?.version && (
|
||||||
|
<p className="text-center text-xs text-text-muted">
|
||||||
|
{t("settings.version", { version: health.version })}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -517,3 +517,8 @@ export interface AppSettings {
|
|||||||
idle_cash_threshold_days: number;
|
idle_cash_threshold_days: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HealthStatus {
|
||||||
|
status: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user