From b29540bee575dbde929a16a3e778d73cd93c364a Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 2 Jul 2026 14:06:32 +0900 Subject: [PATCH] support app uninstall ipc --- src/main/index.ts | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/index.ts b/src/main/index.ts index e0c8da8..b77364a 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,5 +1,5 @@ import { app, shell, BrowserWindow, ipcMain, protocol, net, session as electronSession, dialog } from 'electron' -import { join, dirname, resolve } from 'path' +import { join, dirname, resolve, relative, isAbsolute } from 'path' import { chmodSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'fs' import { get as httpGet } from 'http' import { get as httpsGet } from 'https' @@ -126,6 +126,13 @@ function getVersionFilePath(targetPath: string): string { return `${targetPath}.version` } +function assertUserAppPath(targetPath: string) { + const relativePath = relative(getUserAppsDir(), targetPath) + if (!relativePath || relativePath.startsWith('..') || isAbsolute(relativePath)) { + throw new Error('앱 설치 디렉터리 밖의 파일은 삭제할 수 없습니다.') + } +} + function readInstalledVersion(targetPath?: string): string | null { if (!targetPath) return null const versionFilePath = getVersionFilePath(targetPath) @@ -711,6 +718,35 @@ function createWindow(): void { } }) + ipcMain.handle('uninstall-app', (_, payload: { + appName: string + execPath?: string + }) => { + const targetPath = resolveExecPath(payload.execPath, payload.appName) + if (!targetPath) throw new Error('설치 경로가 설정되지 않았습니다.') + assertUserAppPath(targetPath) + + const existingWin = appWindows.get(payload.appName) + if (existingWin && !existingWin.isDestroyed()) { + existingWin.close() + } + + const existingProc = appProcesses.get(payload.appName) + if (existingProc) { + existingProc.kill() + appProcesses.delete(payload.appName) + } + + rmSync(targetPath, { force: true }) + rmSync(getVersionFilePath(targetPath), { force: true }) + + return { + installed: false, + version: null, + path: targetPath, + } + }) + ipcMain.handle('install-launcher-update', async (event, payload: { downloadUrl: string latestVersion?: string