diff --git a/package.json b/package.json index 9c16bcf..84c6481 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "smartims", "private": true, - "version": "0.3.5", + "version": "0.4.0", "type": "module", "scripts": { "dev": "vite", diff --git a/server/package.json b/server/package.json index ebcaa92..dc436c0 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.3.5", + "version": "0.4.0", "description": "", "main": "index.js", "scripts": { diff --git a/server/routes/system.js b/server/routes/system.js index 1507f41..1d6fed0 100644 --- a/server/routes/system.js +++ b/server/routes/system.js @@ -500,11 +500,10 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res exec(historyCmd, (err, stdout, stderr) => { const lines = stdout ? stdout.trim().split('\n') : []; - const history = lines.map(line => { + const allTags = lines.map(line => { const [tag, subject, body, date] = line.split('|'); if (!tag) return null; - // Parse Type from subject: e.g. "[FEATURE] Title" let type = 'patch'; let title = subject || ''; 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]; } - // Parse changes from body (split by newlines and clean up) const changes = (body || '') .split('\n') .map(c => c.trim()) @@ -532,13 +530,29 @@ router.get('/version/remote', isAuthenticated, hasRole('admin'), async (req, res }; }).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({ current: currentVersion, - latest: latest ? `v${latest.version}` : null, - needsUpdate: latest ? (latest.version !== currentVersion.replace(/^v/, '')) : false, - latestInfo: latest, + latest: latestFromRemote ? `v${latestFromRemote.version}` : null, + needsUpdate: needsUpdate, + latestInfo: needsUpdate ? latestFromRemote : null, history: history }); }); diff --git a/src/platform/pages/VersionPage.tsx b/src/platform/pages/VersionPage.tsx index fd432b7..802531c 100644 --- a/src/platform/pages/VersionPage.tsx +++ b/src/platform/pages/VersionPage.tsx @@ -109,13 +109,12 @@ export function VersionPage() { } }; - // Client/Frontend version fixed at build time - const frontendVersion = '0.3.5'; - const buildDate = '2026-01-24'; + // Source of truth for versioning comes from the API + const currentVersion = remoteInfo?.current || '0.4.0'; + const buildDate = '2026-01-25'; - // Check if update is needed based on frontend version vs remote tag - const needsUpdate = remoteInfo?.latest ? - (remoteInfo.latest.replace(/^v/, '') !== frontendVersion) : false; + // Check if update is needed based on API-supplied needsUpdate flag + const needsUpdate = remoteInfo?.needsUpdate || false; return (
- 현재 버전: v{frontendVersion} → 최신 버전: {remoteInfo?.latest} + 현재 버전: v{currentVersion} → 최신 버전: {remoteInfo?.latest}
{remoteInfo?.latestInfo && (