릴리즈 v0.2.7: 시스템 환경 정보 시각화 및 캐싱 보정

This commit is contained in:
choibk 2026-01-24 19:30:01 +09:00
parent 6ad6084ef2
commit 98b52c390e
5 changed files with 62 additions and 24 deletions

View File

@ -1,7 +1,7 @@
{
"name": "smartims",
"private": true,
"version": "0.2.6",
"version": "0.2.7",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -364,6 +364,9 @@ app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
version: packageJson.version,
node_version: process.version,
platform: process.platform,
arch: process.arch,
timestamp: new Date().toISOString().replace('T', ' ').split('.')[0]
});
});

View File

@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.2.6",
"version": "0.2.7",
"description": "",
"main": "index.js",
"scripts": {

View File

@ -435,18 +435,30 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res
const currentVersion = packageJson.version;
// Run git fetch to update tags from remote
exec('git fetch --tags', (err) => {
exec('git fetch --tags', (err, stdout, stderr) => {
if (err) {
console.error('Git fetch failed:', err);
console.error('Stderr:', stderr);
return res.json({
current: currentVersion,
latest: null,
error: '원격 저장소에 연결할 수 없습니다. Git 설정을 확인하세요.'
error: `원격 저장소 동기화 실패: ${stderr || err.message}`
});
}
// Get the latest tag
exec('git describe --tags $(git rev-list --tags --max-count=1)', (err, stdout) => {
// Get the latest tag (Simplified for cross-platform compatibility)
// git describe --tags --abbrev=0 is more robust across shells
exec('git describe --tags --abbrev=0', (err, stdout, stderr) => {
if (err) {
console.error('Git describe failed:', err);
return res.json({
current: currentVersion,
latest: null,
needsUpdate: false,
error: '태그 정보를 찾을 수 없습니다.'
});
}
const latestTag = stdout ? stdout.trim() : null;
res.json({
current: currentVersion,

View File

@ -6,6 +6,9 @@ import { Info, Cpu, Database, Server, Hash, Calendar, RefreshCw, AlertTriangle,
interface VersionInfo {
status: string;
version: string;
node_version: string;
platform: string;
arch: string;
timestamp: string;
}
@ -27,7 +30,8 @@ export function VersionPage() {
const fetchVersion = async () => {
setLoading(true);
try {
const res = await apiClient.get('/health');
// Add timestamp to prevent caching
const res = await apiClient.get(`/health?t=${Date.now()}`);
setHealthInfo(res.data);
} catch (err) {
console.error('Failed to fetch version info', err);
@ -75,10 +79,14 @@ export function VersionPage() {
}
};
// Use values from healthIcon if available, otherwise fallback to local constants
const currentVersion = healthIcon?.version || '0.2.6';
// Client/Frontend version fixed at build time
const frontendVersion = '0.2.7';
const buildDate = '2026-01-24';
// Check if update is needed based on frontend version vs remote tag
const needsUpdate = remoteInfo?.latest ?
(remoteInfo.latest.replace(/^v/, '') !== frontendVersion) : false;
return (
<div className="page-container p-6 max-w-4xl mx-auto">
<div className="flex justify-between items-start mb-8">
@ -96,8 +104,8 @@ export function VersionPage() {
</button>
</div>
{/* Update Alert Banner */}
{remoteInfo?.needsUpdate && !updateResult && (
{/* Update Alert Banner - Based on Frontend Version comparison */}
{needsUpdate && !updateResult && (
<div className="mb-8 p-4 bg-amber-50 border border-amber-200 rounded-xl flex items-center justify-between animate-in fade-in slide-in-from-top-4 duration-500">
<div className="flex items-center gap-3">
<div className="p-2 bg-amber-100 text-amber-600 rounded-lg">
@ -105,7 +113,7 @@ export function VersionPage() {
</div>
<div>
<h4 className="font-bold text-amber-900"> !</h4>
<p className="text-sm text-amber-700"> 버전: v{currentVersion} : <span className="font-bold">{remoteInfo.latest}</span></p>
<p className="text-sm text-amber-700"> 버전: v{frontendVersion} : <span className="font-bold">{remoteInfo?.latest}</span></p>
</div>
</div>
<button
@ -146,7 +154,7 @@ export function VersionPage() {
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-slate-50">
<span className="text-slate-500 text-sm flex items-center gap-2"><Info size={14} /> </span>
<span className="font-bold text-indigo-600 bg-indigo-50 px-3 py-1 rounded-full text-xs">v{currentVersion}</span>
<span className="font-bold text-indigo-600 bg-indigo-50 px-3 py-1 rounded-full text-xs">v{frontendVersion}</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-slate-50">
<span className="text-slate-500 text-sm flex items-center gap-2"><Calendar size={14} /> </span>
@ -176,19 +184,25 @@ export function VersionPage() {
<div className="space-y-4">
<div className="flex justify-between items-center py-2 border-b border-slate-50">
<span className="text-slate-500 text-sm flex items-center gap-2"><Info size={14} /> API </span>
<span className="font-bold text-emerald-600 bg-emerald-50 px-3 py-1 rounded-full text-xs">
{loading ? 'Checking...' : healthIcon?.version ? `v${healthIcon.version}` : 'N/A'}
<span className="text-slate-500 text-sm flex items-center gap-2"><Cpu size={14} /> </span>
<span className="font-medium text-slate-700 text-sm">
{loading ? 'Checking...' : healthIcon?.node_version ? `Node.js ${healthIcon.node_version}` : 'N/A'}
</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-slate-50">
<span className="text-slate-500 text-sm flex items-center gap-2"><Server size={14} /> </span>
<span className="font-medium text-slate-700 text-sm uppercase">
{loading ? '...' : healthIcon?.platform ? `${healthIcon.platform} (${healthIcon.arch})` : 'Unknown'}
</span>
</div>
<div className="flex justify-between items-center py-2 border-b border-slate-50">
<span className="text-slate-500 text-sm flex items-center gap-2"><Calendar size={14} /> </span>
<span className="font-medium text-slate-700 text-sm truncate max-w-[150px]">
<span className="font-medium text-slate-700 text-sm">
{loading ? '...' : healthIcon?.timestamp || 'Unknown'}
</span>
</div>
<div className="flex justify-between items-center py-2">
<span className="text-slate-500 text-sm flex items-center gap-2"><Server size={14} /> </span>
<span className="text-slate-500 text-sm flex items-center gap-2"><RefreshCw size={14} /> </span>
<span className={`font-bold text-xs uppercase ${healthIcon?.status === 'ok' ? 'text-emerald-500' : 'text-red-500'}`}>
{loading ? '...' : healthIcon?.status === 'ok' ? 'Running' : 'Offline'}
</span>
@ -219,17 +233,26 @@ export function VersionPage() {
<div className="space-y-4">
{[
{
version: '0.2.5',
version: '0.2.7',
date: '2026-01-24',
title: '플랫폼 보안 모듈 및 시스템 자동 업데이트 엔진 도입',
title: '시스템 환경 정보 시각화 및 캐싱 보정',
changes: [
'Git Tag 기반 시스템 자동 업데이트 관리 모듈 신규 도입',
'최고관리자 전용 업데이트 실행 UI 구축',
'데이터베이스 및 암호화 마스터 키 자가 관리 엔진 고도화',
'사용자 관리 UI 디자인 및 권한 계층 로직 일원화'
'백엔드 서비스 엔진 카드에 실제 런타임(Node.js) 및 OS 정보 표시',
'버전 정보 조회 시 브라우저 API 캐싱 보정 로직 적용',
'플랫폼 업데이트 판단 기준을 프론트엔드 빌드 버전으로 일원화'
],
type: 'feature'
},
{
version: '0.2.6',
date: '2026-01-24',
title: '시스템 자동 업데이트 엔진 도입 (Hotfix)',
changes: [
'Git Tag 기반 시스템 자동 업데이트 관리 모듈 신규 도입',
'최고관리자 전용 업데이트 실행 UI 구축'
],
type: 'fix'
},
{
version: '0.2.1',
date: '2026-01-22',