feat(backup): wire BackupService — whenReady + before-quit + tray
Instantiate BackupService at app.whenReady, run daily snapshot then again before quit (synchronous-blocking via preventDefault). Tray menu gets '지금 백업' entry that triggers manual runDaily with native toast feedback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
} from './windows/quickCaptureWindow.js';
|
||||
import { createTray } from './tray.js';
|
||||
import { MediaGc } from './services/MediaGc.js';
|
||||
import { BackupService } from './services/BackupService.js';
|
||||
|
||||
const HIDDEN_ARG = '--hidden';
|
||||
const startedHidden = process.argv.includes(HIDDEN_ARG);
|
||||
@@ -96,19 +97,55 @@ app.whenReady().then(async () => {
|
||||
createInboxWindow();
|
||||
}
|
||||
createQuickCaptureWindow();
|
||||
createTray(
|
||||
() => createInboxWindow(),
|
||||
() => showQuickCapture()
|
||||
);
|
||||
|
||||
await worker.loadFromDb();
|
||||
|
||||
const gc = new MediaGc(db, store);
|
||||
void gc.run().then((r) => logger.info('media.gc', { ...r } as Record<string, unknown>));
|
||||
|
||||
const backup = new BackupService(db, join(paths.profileDir, 'backups'));
|
||||
void backup.runDaily()
|
||||
.then((r) => logger.info('backup.daily', { ...r } as Record<string, unknown>))
|
||||
.catch((e) => logger.warn('backup.daily.failed', { reason: String(e) }));
|
||||
|
||||
let backupOnQuitDone = false;
|
||||
app.on('before-quit', (e) => {
|
||||
if (backupOnQuitDone) return;
|
||||
e.preventDefault();
|
||||
backup.runDaily()
|
||||
.then((r) => logger.info('backup.beforeQuit', { ...r } as Record<string, unknown>))
|
||||
.catch((e2) => logger.warn('backup.beforeQuit.failed', { reason: String(e2) }))
|
||||
.finally(() => {
|
||||
backupOnQuitDone = true;
|
||||
app.isQuitting = true;
|
||||
app.quit();
|
||||
});
|
||||
});
|
||||
|
||||
createTray(
|
||||
() => createInboxWindow(),
|
||||
() => showQuickCapture(),
|
||||
async () => {
|
||||
try {
|
||||
const r = await backup.runDaily();
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: r.snapshotted
|
||||
? `백업 완료 — ${r.removed?.length ?? 0}개 정리`
|
||||
: `오늘 백업이 이미 있습니다`,
|
||||
silent: true
|
||||
}).show();
|
||||
} catch (e) {
|
||||
logger.warn('backup.manual.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '백업을 만들지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createInboxWindow();
|
||||
});
|
||||
});
|
||||
|
||||
app.on('before-quit', () => { app.isQuitting = true; });
|
||||
|
||||
@@ -4,11 +4,16 @@ const { app, Tray, Menu, nativeImage } = electron;
|
||||
|
||||
let tray: TrayType | null = null;
|
||||
|
||||
function buildMenu(showInbox: () => void, showCapture: () => void) {
|
||||
function buildMenu(
|
||||
showInbox: () => void,
|
||||
showCapture: () => void,
|
||||
runBackup: () => void
|
||||
) {
|
||||
const items: MenuItemConstructorOptions[] = [
|
||||
{ label: '구출한 메모 보기', click: showInbox },
|
||||
{ label: '기억 구출하기', click: showCapture },
|
||||
{ type: 'separator' }
|
||||
{ type: 'separator' },
|
||||
{ label: '지금 백업', click: runBackup }
|
||||
];
|
||||
if (app.isPackaged) {
|
||||
const { openAtLogin } = app.getLoginItemSettings();
|
||||
@@ -24,16 +29,22 @@ function buildMenu(showInbox: () => void, showCapture: () => void) {
|
||||
}
|
||||
});
|
||||
items.push({ type: 'separator' });
|
||||
} else {
|
||||
items.push({ type: 'separator' });
|
||||
}
|
||||
items.push({ label: '종료', click: () => { app.isQuitting = true; app.quit(); } });
|
||||
return Menu.buildFromTemplate(items);
|
||||
}
|
||||
|
||||
export function createTray(showInbox: () => void, showCapture: () => void): TrayType {
|
||||
export function createTray(
|
||||
showInbox: () => void,
|
||||
showCapture: () => void,
|
||||
runBackup: () => void
|
||||
): TrayType {
|
||||
const icon = nativeImage.createEmpty();
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip('Inkling');
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture));
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture, runBackup));
|
||||
tray.on('click', showInbox);
|
||||
return tray;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user