mirror of
https://github.com/jung-geun/NFD2NFC.git
synced 2026-06-21 12:25:14 +09:00
tsconfig.json: module ESNext로 변경, renderer 제외. .eslintrc.cjs: varsIgnorePattern '^_' 추가. store.ts: notificationIntervalSecs 기본값(30) 추가. src/lib/index.ts: FilterOptions/ScanEntry를 올바른 소스 파일에서 import. scanner.ts: 미사용 fs import 제거. ipc.ts: 미사용 nanoid import 제거. cli/index.ts: 미사용 argv 변수 void 처리, showHelp() → 메시지 출력으로 대체. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
126 lines
4.3 KiB
TypeScript
126 lines
4.3 KiB
TypeScript
import { ipcMain, dialog, app, BrowserWindow } from 'electron';
|
|
import { randomUUID } from 'crypto';
|
|
import { scan } from '../core/scanner';
|
|
import { normalizeEntry } from '../core/normalizer';
|
|
import * as store from './store';
|
|
import * as watcher from './watcher';
|
|
import type { WatchedDir, UndoEntry } from '../core/types';
|
|
|
|
export function registerIpcHandlers(): void {
|
|
// ── 디렉토리 선택 ──
|
|
ipcMain.handle('dialog:selectDirectory', async (event) => {
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
|
const result = await dialog.showOpenDialog(win!, { properties: ['openDirectory'] });
|
|
return result.canceled ? null : result.filePaths[0];
|
|
});
|
|
|
|
// ── 디렉토리 CRUD ──
|
|
ipcMain.handle('dirs:list', async () => store.getDirs());
|
|
|
|
ipcMain.handle('dirs:add', async (_e, dirPath: string) => {
|
|
const settings = await store.getSettings();
|
|
const dir: WatchedDir = {
|
|
id: randomUUID(),
|
|
path: dirPath,
|
|
recursive: false,
|
|
mode: settings.defaultMode,
|
|
enabled: true,
|
|
customRanges: [],
|
|
};
|
|
await store.addDir(dir);
|
|
await watcher.startDir(dir);
|
|
return dir;
|
|
});
|
|
|
|
ipcMain.handle('dirs:update', async (_e, id: string, patch: Partial<WatchedDir>) => {
|
|
await store.updateDir(id, patch);
|
|
// 모드/recursive 변경 시 재시작
|
|
if ('mode' in patch || 'recursive' in patch || 'enabled' in patch || 'customRanges' in patch) {
|
|
await watcher.stopDir(id);
|
|
const dirs = await store.getDirs();
|
|
const dir = dirs.find((d) => d.id === id);
|
|
if (dir?.enabled) await watcher.startDir(dir);
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('dirs:remove', async (_e, id: string) => {
|
|
await watcher.stopDir(id);
|
|
await store.removeDir(id);
|
|
});
|
|
|
|
// ── Dry-run 스캔 ──
|
|
ipcMain.handle('dirs:scan', async (_e, id: string) => {
|
|
const dirs = await store.getDirs();
|
|
const dir = dirs.find((d) => d.id === id);
|
|
if (!dir) return [];
|
|
return scan(dir.path, dir.recursive, { customRanges: dir.customRanges });
|
|
});
|
|
|
|
// ── 수동 모드 대기 큐 적용 ──
|
|
ipcMain.handle('dirs:applyQueue', async (_e, id: string) => {
|
|
const dirs = await store.getDirs();
|
|
const dir = dirs.find((d) => d.id === id);
|
|
if (!dir) return [];
|
|
return watcher.applyManualQueue(id, dir);
|
|
});
|
|
|
|
ipcMain.handle('dirs:pendingQueue', async (_e, id: string) => {
|
|
return watcher.getPendingQueue(id);
|
|
});
|
|
|
|
// ── 감시 일시정지/재개/상태 ──
|
|
ipcMain.handle('watcher:status', () => ({ paused: watcher.isGloballyPaused() }));
|
|
|
|
ipcMain.handle('watcher:pauseAll', async () => {
|
|
await watcher.pauseAll();
|
|
});
|
|
|
|
ipcMain.handle('watcher:resumeAll', async () => {
|
|
const dirs = await store.getDirs();
|
|
await watcher.resumeAll(dirs);
|
|
});
|
|
|
|
// ── Undo ──
|
|
ipcMain.handle('undo:list', async () => store.getUndoLog());
|
|
|
|
ipcMain.handle('undo:revertEntry', async (_e, entry: UndoEntry) => {
|
|
try {
|
|
const result = await normalizeEntry(entry.newPath, 'file');
|
|
// 실제 되돌리기: newPath → oldPath
|
|
const fs = await import('fs/promises');
|
|
await fs.rename(entry.newPath, entry.oldPath);
|
|
await store.markUndoReverted([entry.id]);
|
|
return { success: true, result };
|
|
} catch (err) {
|
|
return { success: false, error: String(err) };
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('undo:revertLastBatch', async () => {
|
|
const log = await store.getUndoLog();
|
|
if (!log.length) return [];
|
|
const lastTs = log[log.length - 1].ts;
|
|
// 마지막 5초 이내의 항목을 한 배치로 묶음
|
|
const batch = log.filter((e) => !e.reverted && lastTs - e.ts < 5000);
|
|
const fs = await import('fs/promises');
|
|
const results = [];
|
|
for (const entry of [...batch].reverse()) {
|
|
try {
|
|
await fs.rename(entry.newPath, entry.oldPath);
|
|
results.push({ ...entry, success: true });
|
|
} catch (err) {
|
|
results.push({ ...entry, success: false, error: String(err) });
|
|
}
|
|
}
|
|
await store.markUndoReverted(batch.map((e) => e.id));
|
|
return results;
|
|
});
|
|
|
|
// ── 설정 ──
|
|
ipcMain.handle('settings:get', async () => store.getSettings());
|
|
ipcMain.handle('settings:update', async (_e, patch) => store.updateSettings(patch));
|
|
|
|
// ── 앱 정보 ──
|
|
ipcMain.handle('app:version', () => app.getVersion());
|
|
}
|