d3ro-voice/apps/desktop/src/main/ipc/cloud-sync-handlers.ts
Yun Chan aef44289a5 refactor(main): IPC 채널 SSOT 일원화 + AppConfig 타입 강화 (WS2)
- ipc-channels.ts SSOT에 9개 그룹/키 추가: INSTRUCTION, SYSTEM_AUDIO,
  POPUP_RESULT/HISTORY/COMMAND/CAPTION, VOICE_PARTIAL, CLIPBOARD, APP,
  VOICE_CONVERSATION.FINISH_LISTENING
- WindowManager/handlers/bootstrap 하드코딩 채널 -> IPC_CHANNELS 교체.
  불일치 4건(result:* vs window:tip*)은 preload 재검증 후 SSOT로 통일.
  notifyRenderer channel: string -> IPCChannel 타입 좁힘.
- AppConfig에 7개 누락 키 추가(customInstructions, llmChains,
  voiceCommandRules, voiceCommandsEnabled, activeInstructionId,
  activeChainId, captionAudioSource) -> as never 16건 제거.
- ChainService/CustomInstructionService/WindowManager dynamic require -> static import.
- services/index.ts 데드 레지스트리 제거 (import 0건).
SKIP: ipcSuccess/ipcError 헬퍼 통일, catch 패턴(별도)
정책: docs/REFACTOR_POLICY.md DP2, DP9
2026-07-22 01:54:18 +09:00

114 lines
3.1 KiB
TypeScript

// src/main/ipc/cloud-sync-handlers.ts
// Phase V2-4: CloudSync IPC 핸들러
import { ipcMain, BrowserWindow } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
import type { IPCResult } from '@d3ro/core/errors'
import { getCloudSyncService } from '../services/CloudSyncService'
import { getLogger } from '../services/LoggerService'
import { configSet } from '../services/ConfigService'
const logger = getLogger('cloud-sync-handlers')
interface SignInParams {
provider: 'google' | 'github'
}
function ok<T>(data: T): IPCResult<T> {
return { success: true, data }
}
function fail(error: unknown): IPCResult<never> {
if (error instanceof D3ROError) {
return { success: false, error: { code: error.code, message: error.message } }
}
const message = error instanceof Error ? error.message : String(error)
return {
success: false,
error: { code: ErrorCode.LLMProcessingFailed, message }
}
}
export function registerCloudSyncHandlers(): void {
const sync = getCloudSyncService()
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.GET_STATE, () => {
try {
return ok(sync.getState())
} catch (e) {
return fail(e)
}
})
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.SIGN_IN, async (_e, params: SignInParams) => {
try {
await sync.startSignIn(params.provider)
return ok({ started: true })
} catch (e) {
return fail(e)
}
})
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.HANDLE_CALLBACK, async (_e, params: { code: string }) => {
try {
await sync.handleAuthCallback(params.code)
return ok(sync.getState())
} catch (e) {
return fail(e)
}
})
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.SIGN_OUT, async () => {
try {
await sync.signOut()
return ok(sync.getState())
} catch (e) {
return fail(e)
}
})
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.PUSH_ALL, async () => {
try {
const result = await sync.pushAll()
configSet('cloudSyncLastAt', Date.now())
return ok(result)
} catch (e) {
return fail(e)
}
})
ipcMain.handle(IPC_CHANNELS.CLOUD_SYNC.PULL_ALL, async () => {
try {
const result = await sync.pullAll()
configSet('cloudSyncLastAt', Date.now())
return ok(result)
} catch (e) {
return fail(e)
}
})
// 이벤트 → renderer 브로드캐스트
function broadcast(channel: string, payload: unknown): void {
BrowserWindow.getAllWindows().forEach((win) => {
if (!win.isDestroyed()) {
win.webContents.send(channel, payload)
}
})
}
sync.on('auth-changed', (payload) => {
broadcast(IPC_CHANNELS.CLOUD_SYNC.AUTH_CHANGED, payload)
})
sync.on('sync-progress', (payload) => {
broadcast(IPC_CHANNELS.CLOUD_SYNC.SYNC_PROGRESS, payload)
})
sync.on('sync-complete', (payload) => {
broadcast(IPC_CHANNELS.CLOUD_SYNC.SYNC_COMPLETE, payload)
})
sync.on('sync-error', (payload) => {
broadcast(IPC_CHANNELS.CLOUD_SYNC.SYNC_ERROR, payload)
})
logger.info('Cloud Sync IPC handlers registered')
}