버전 관리 시스템 개선: 히스토리 필터링 로직 도입, 하드코딩 제거 및 업데이트 안내 가시성 강화
This commit is contained in:
parent
bc16a84db5
commit
9605e65943
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "smartims",
|
"name": "smartims",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.3.5",
|
"version": "0.4.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.3.5",
|
"version": "0.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -500,11 +500,10 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res
|
|||||||
|
|
||||||
exec(historyCmd, (err, stdout, stderr) => {
|
exec(historyCmd, (err, stdout, stderr) => {
|
||||||
const lines = stdout ? stdout.trim().split('\n') : [];
|
const lines = stdout ? stdout.trim().split('\n') : [];
|
||||||
const history = lines.map(line => {
|
const allTags = lines.map(line => {
|
||||||
const [tag, subject, body, date] = line.split('|');
|
const [tag, subject, body, date] = line.split('|');
|
||||||
if (!tag) return null;
|
if (!tag) return null;
|
||||||
|
|
||||||
// Parse Type from subject: e.g. "[FEATURE] Title"
|
|
||||||
let type = 'patch';
|
let type = 'patch';
|
||||||
let title = subject || '';
|
let title = subject || '';
|
||||||
const typeMatch = (subject || '').match(/^\[(URGENT|FEATURE|FIX|PATCH|HOTFIX)\]\s*(.*)$/i);
|
const typeMatch = (subject || '').match(/^\[(URGENT|FEATURE|FIX|PATCH|HOTFIX)\]\s*(.*)$/i);
|
||||||
@ -516,7 +515,6 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res
|
|||||||
title = typeMatch[2];
|
title = typeMatch[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse changes from body (split by newlines and clean up)
|
|
||||||
const changes = (body || '')
|
const changes = (body || '')
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(c => c.trim())
|
.map(c => c.trim())
|
||||||
@ -532,13 +530,29 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res
|
|||||||
};
|
};
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
const latest = history[0] || null;
|
// Version Comparison Helper
|
||||||
|
const compareVersions = (v1, v2) => {
|
||||||
|
const parts1 = v1.split('.').map(Number);
|
||||||
|
const parts2 = v2.split('.').map(Number);
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
if (parts1[i] > parts2[i]) return 1;
|
||||||
|
if (parts1[i] < parts2[i]) return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Filter 1: History is ONLY versions <= current
|
||||||
|
const history = allTags.filter(t => compareVersions(t.version, currentVersion) <= 0);
|
||||||
|
|
||||||
|
// Filter 2: Latest is the absolute newest tag available (could be > current)
|
||||||
|
const latestFromRemote = allTags[0] || null;
|
||||||
|
const needsUpdate = latestFromRemote ? (compareVersions(latestFromRemote.version, currentVersion) > 0) : false;
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
current: currentVersion,
|
current: currentVersion,
|
||||||
latest: latest ? `v${latest.version}` : null,
|
latest: latestFromRemote ? `v${latestFromRemote.version}` : null,
|
||||||
needsUpdate: latest ? (latest.version !== currentVersion.replace(/^v/, '')) : false,
|
needsUpdate: needsUpdate,
|
||||||
latestInfo: latest,
|
latestInfo: needsUpdate ? latestFromRemote : null,
|
||||||
history: history
|
history: history
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -109,13 +109,12 @@ export function VersionPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Client/Frontend version fixed at build time
|
// Source of truth for versioning comes from the API
|
||||||
const frontendVersion = '0.3.5';
|
const currentVersion = remoteInfo?.current || '0.4.0';
|
||||||
const buildDate = '2026-01-24';
|
const buildDate = '2026-01-25';
|
||||||
|
|
||||||
// Check if update is needed based on frontend version vs remote tag
|
// Check if update is needed based on API-supplied needsUpdate flag
|
||||||
const needsUpdate = remoteInfo?.latest ?
|
const needsUpdate = remoteInfo?.needsUpdate || false;
|
||||||
(remoteInfo.latest.replace(/^v/, '') !== frontendVersion) : false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container p-6 max-w-4xl mx-auto">
|
<div className="page-container p-6 max-w-4xl mx-auto">
|
||||||
@ -152,7 +151,7 @@ export function VersionPage() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className={`text-sm font-medium ${remoteInfo?.latestInfo?.type === 'urgent' ? 'text-red-700' : 'text-amber-700'}`}>
|
<p className={`text-sm font-medium ${remoteInfo?.latestInfo?.type === 'urgent' ? 'text-red-700' : 'text-amber-700'}`}>
|
||||||
현재 버전: v{frontendVersion} → 최신 버전: <span className="font-black underline underline-offset-4">{remoteInfo?.latest}</span>
|
현재 버전: v{currentVersion} → 최신 버전: <span className="font-black underline underline-offset-4">{remoteInfo?.latest}</span>
|
||||||
</p>
|
</p>
|
||||||
{remoteInfo?.latestInfo && (
|
{remoteInfo?.latestInfo && (
|
||||||
<div className={`mt-3 p-3 rounded-lg border text-xs leading-relaxed ${remoteInfo?.latestInfo?.type === 'urgent' ? 'bg-white/50 border-red-100 text-red-800' : 'bg-white/50 border-amber-100 text-amber-800'}`}>
|
<div className={`mt-3 p-3 rounded-lg border text-xs leading-relaxed ${remoteInfo?.latestInfo?.type === 'urgent' ? 'bg-white/50 border-red-100 text-red-800' : 'bg-white/50 border-amber-100 text-amber-800'}`}>
|
||||||
@ -209,7 +208,7 @@ export function VersionPage() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between items-center py-2 border-b border-slate-50">
|
<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="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{frontendVersion}</span>
|
<span className="font-bold text-indigo-600 bg-indigo-50 px-3 py-1 rounded-full text-xs">v{currentVersion}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center py-2 border-b border-slate-50">
|
<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="text-slate-500 text-sm flex items-center gap-2"><Calendar size={14} /> 빌드 일자</span>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user