mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
FEATURE (version): Reload frontend if faced version mismatch with backend
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"databasus-backend/internal/features/restores/restoring"
|
||||
"databasus-backend/internal/features/storages"
|
||||
system_healthcheck "databasus-backend/internal/features/system/healthcheck"
|
||||
system_version "databasus-backend/internal/features/system/version"
|
||||
task_cancellation "databasus-backend/internal/features/tasks/cancellation"
|
||||
users_controllers "databasus-backend/internal/features/users/controllers"
|
||||
users_middleware "databasus-backend/internal/features/users/middleware"
|
||||
@@ -210,6 +211,7 @@ func setUpRoutes(r *gin.Engine) {
|
||||
userController := users_controllers.GetUserController()
|
||||
userController.RegisterRoutes(v1)
|
||||
system_healthcheck.GetHealthcheckController().RegisterRoutes(v1)
|
||||
system_version.GetVersionController().RegisterRoutes(v1)
|
||||
backups_controllers.GetBackupController().RegisterPublicRoutes(v1)
|
||||
backups_controllers.GetPostgresWalBackupController().RegisterRoutes(v1)
|
||||
databases.GetDatabaseController().RegisterPublicRoutes(v1)
|
||||
|
||||
30
backend/internal/features/system/version/controller.go
Normal file
30
backend/internal/features/system/version/controller.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package system_version
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type VersionController struct{}
|
||||
|
||||
func (c *VersionController) RegisterRoutes(router *gin.RouterGroup) {
|
||||
router.GET("/system/version", c.GetVersion)
|
||||
}
|
||||
|
||||
// GetVersion
|
||||
// @Summary Get application version
|
||||
// @Description Returns the current application version
|
||||
// @Tags system/version
|
||||
// @Produce json
|
||||
// @Success 200 {object} VersionResponse
|
||||
// @Router /system/version [get]
|
||||
func (c *VersionController) GetVersion(ctx *gin.Context) {
|
||||
version := os.Getenv("APP_VERSION")
|
||||
if version == "" {
|
||||
version = "dev"
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, VersionResponse{Version: version})
|
||||
}
|
||||
7
backend/internal/features/system/version/di.go
Normal file
7
backend/internal/features/system/version/di.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package system_version
|
||||
|
||||
var versionController = &VersionController{}
|
||||
|
||||
func GetVersionController() *VersionController {
|
||||
return versionController
|
||||
}
|
||||
5
backend/internal/features/system/version/dto.go
Normal file
5
backend/internal/features/system/version/dto.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package system_version
|
||||
|
||||
type VersionResponse struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
@@ -3,6 +3,8 @@ import { useEffect, useState } from 'react';
|
||||
import { BrowserRouter, Route } from 'react-router';
|
||||
import { Routes } from 'react-router';
|
||||
|
||||
import { useVersionCheck } from './shared/hooks/useVersionCheck';
|
||||
|
||||
import { userApi } from './entity/users';
|
||||
import { AuthPageComponent } from './pages/AuthPageComponent';
|
||||
import { OAuthCallbackPage } from './pages/OAuthCallbackPage';
|
||||
@@ -14,6 +16,8 @@ function AppContent() {
|
||||
const [isAuthorized, setIsAuthorized] = useState(false);
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
useVersionCheck();
|
||||
|
||||
useEffect(() => {
|
||||
const isAuthorized = userApi.isAuthorized();
|
||||
setIsAuthorized(isAuthorized);
|
||||
|
||||
14
frontend/src/entity/system/api/systemApi.ts
Normal file
14
frontend/src/entity/system/api/systemApi.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getApplicationServer } from '../../../constants';
|
||||
import type { VersionResponse } from '../model/VersionResponse';
|
||||
|
||||
export const systemApi = {
|
||||
async getVersion(): Promise<VersionResponse> {
|
||||
const response = await fetch(`${getApplicationServer()}/api/v1/system/version`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch version: ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
};
|
||||
2
frontend/src/entity/system/index.ts
Normal file
2
frontend/src/entity/system/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { systemApi } from './api/systemApi';
|
||||
export { type VersionResponse } from './model/VersionResponse';
|
||||
3
frontend/src/entity/system/model/VersionResponse.ts
Normal file
3
frontend/src/entity/system/model/VersionResponse.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type VersionResponse = {
|
||||
version: string;
|
||||
};
|
||||
40
frontend/src/shared/hooks/useVersionCheck.ts
Normal file
40
frontend/src/shared/hooks/useVersionCheck.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { APP_VERSION } from '../../constants';
|
||||
import { systemApi } from '../../entity/system';
|
||||
|
||||
const VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1000;
|
||||
const RELOAD_COOLDOWN_MS = 10 * 1000;
|
||||
const LAST_RELOAD_KEY = 'lastVersionReload';
|
||||
|
||||
export function useVersionCheck() {
|
||||
useEffect(() => {
|
||||
if (APP_VERSION === 'dev') {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkVersion = async () => {
|
||||
try {
|
||||
const { version } = await systemApi.getVersion();
|
||||
|
||||
if (version && version !== APP_VERSION) {
|
||||
const lastReload = Number(localStorage.getItem(LAST_RELOAD_KEY) || '0');
|
||||
|
||||
if (Date.now() - lastReload < RELOAD_COOLDOWN_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(LAST_RELOAD_KEY, String(Date.now()));
|
||||
window.location.reload();
|
||||
}
|
||||
} catch {
|
||||
// Silently ignore errors — network issues shouldn't break the app
|
||||
}
|
||||
};
|
||||
|
||||
checkVersion();
|
||||
const interval = setInterval(checkVersion, VERSION_CHECK_INTERVAL_MS);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
}
|
||||
Reference in New Issue
Block a user