수정
This commit is contained in:
@@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user