This commit is contained in:
Macbook
2026-06-14 11:35:54 +09:00
parent 117a588df8
commit 78cda775ad
8 changed files with 519 additions and 63 deletions
+144 -5
View File
@@ -2,25 +2,164 @@ import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
import { getVersion } from '@tauri-apps/api/app';
export type DesktopUpdatePhase =
| 'checking'
| 'uptodate'
| 'available'
| 'downloading'
| 'installing'
| 'relaunching'
| 'error';
export interface DesktopUpdateProgress {
phase: DesktopUpdatePhase;
message: string;
currentVersion?: string;
newVersion?: string;
downloadPercent?: number;
downloadedBytes?: number;
totalBytes?: number;
}
export interface DesktopUpdateResult {
ok: boolean;
phase: DesktopUpdatePhase;
message: string;
currentVersion?: string;
newVersion?: string;
}
function emit(onProgress: ((p: DesktopUpdateProgress) => void) | undefined, p: DesktopUpdateProgress) {
onProgress?.(p);
}
function formatUpdateError(e: unknown): string {
const msg = e instanceof Error ? e.message : String(e ?? '');
const lower = msg.toLowerCase();
if (!msg.trim()) return '업데이트 확인에 실패했습니다.';
if (/json|parse|unexpected end|eof|empty/.test(lower)) {
return '서버 업데이트 정보(latest.json)를 읽을 수 없습니다. PC 프로그램 탭에서 새 dmg를 받아 주세요.';
}
if (/signature|pubkey|verify|minisign|invalid key/.test(lower)) {
return '업데이트 서명 검증에 실패했습니다. 웹 PC 프로그램 탭에서 설치 파일을 다시 받아 주세요.';
}
if (/404|not found/.test(lower)) {
return '업데이트 파일을 찾을 수 없습니다. 관리자에게 문의하거나 dmg를 직접 다운로드해 주세요.';
}
if (/network|fetch|connect|timeout|dns|offline|failed to send/.test(lower)) {
return `네트워크 오류로 업데이트를 확인하지 못했습니다. (${msg})`;
}
if (/platform|darwin|arch|target/.test(lower)) {
return `이 PC용 업데이트 패키지가 없습니다. (${msg})`;
}
return msg;
}
export async function getAppVersion(): Promise<string> {
return getVersion();
}
/** @deprecated runDesktopUpdate 사용 */
export async function checkForUpdates(): Promise<{
available: boolean;
version?: string;
message?: string;
}> {
const result = await runDesktopUpdate();
return {
available: result.phase === 'relaunching' || result.phase === 'available',
version: result.newVersion,
message: result.message,
};
}
export async function runDesktopUpdate(
onProgress?: (p: DesktopUpdateProgress) => void,
): Promise<DesktopUpdateResult> {
let currentVersion = '';
try {
currentVersion = await getVersion();
} catch {
currentVersion = '—';
}
emit(onProgress, {
phase: 'checking',
message: '서버에서 새 버전을 확인하는 중…',
currentVersion,
});
try {
const update = await check();
if (!update) {
return { available: false, message: '최신 버전입니다.' };
const message = `최신 버전입니다. (v${currentVersion})`;
emit(onProgress, { phase: 'uptodate', message, currentVersion });
return { ok: true, phase: 'uptodate', message, currentVersion };
}
await update.downloadAndInstall();
emit(onProgress, {
phase: 'available',
message: `새 버전 v${update.version}을(를) 받습니다. (현재 v${currentVersion})`,
currentVersion,
newVersion: update.version,
});
let downloaded = 0;
let total: number | undefined;
await update.downloadAndInstall(event => {
if (event.event === 'Started') {
total = event.data.contentLength;
downloaded = 0;
emit(onProgress, {
phase: 'downloading',
message: total ? '다운로드 중… (0%)' : '다운로드 중…',
currentVersion,
newVersion: update.version,
downloadPercent: 0,
downloadedBytes: 0,
totalBytes: total,
});
} else if (event.event === 'Progress') {
downloaded += event.data.chunkLength;
const pct =
total && total > 0 ? Math.min(99, Math.round((downloaded / total) * 100)) : undefined;
emit(onProgress, {
phase: 'downloading',
message: pct != null ? `다운로드 중… (${pct}%)` : '다운로드 중…',
currentVersion,
newVersion: update.version,
downloadPercent: pct,
downloadedBytes: downloaded,
totalBytes: total,
});
} else if (event.event === 'Finished') {
emit(onProgress, {
phase: 'installing',
message: '업데이트 패키지 설치 중…',
currentVersion,
newVersion: update.version,
});
}
});
emit(onProgress, {
phase: 'relaunching',
message: '설치 완료. 앱을 다시 시작합니다…',
currentVersion,
newVersion: update.version,
});
await relaunch();
return { available: true, version: update.version, message: '업데이트 설치 후 재시작합니다.' };
return {
ok: true,
phase: 'relaunching',
message: '재시작 중…',
currentVersion,
newVersion: update.version,
};
} catch (e) {
const msg = e instanceof Error ? e.message : '업데이트 확인 실패';
return { available: false, message: msg };
const message = formatUpdateError(e);
emit(onProgress, { phase: 'error', message, currentVersion });
return { ok: false, phase: 'error', message, currentVersion };
}
}