diff --git a/src/preload/api/auth.ts b/src/preload/api/auth.ts new file mode 100644 index 0000000..eaf99d7 --- /dev/null +++ b/src/preload/api/auth.ts @@ -0,0 +1,15 @@ +import type { LoginRequest } from '../../main/types/auth-ipc.types' +import type { UserInfo } from '../../main/types/user.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc } from '../lib/ipc' + +export const authApi = { + getComputerName: () => invokeIpc(IPC_CHANNELS.AUTH_GET_COMPUTER_NAME), + silentLogin: () => invokeIpc(IPC_CHANNELS.AUTH_SILENT_LOGIN), + login: (request: LoginRequest) => invokeIpc(IPC_CHANNELS.AUTH_LOGIN, request), + logout: () => invokeIpc(IPC_CHANNELS.AUTH_LOGOUT), + getCurrentUser: () => invokeIpc(IPC_CHANNELS.AUTH_GET_CURRENT_USER), + getAllUsers: () => invokeIpc(IPC_CHANNELS.AUTH_GET_ALL_USERS), + switchUser: (userInfo: UserInfo) => invokeIpc(IPC_CHANNELS.AUTH_SWITCH_USER, userInfo), + isAdmin: () => invokeIpc(IPC_CHANNELS.AUTH_IS_ADMIN) +} as const diff --git a/src/preload/api/cleaner.ts b/src/preload/api/cleaner.ts new file mode 100644 index 0000000..3a26226 --- /dev/null +++ b/src/preload/api/cleaner.ts @@ -0,0 +1,19 @@ +import type { + CleanerInput, + CleanerProgress, + ExportResultItem +} from '../../main/types/cleaner.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc, ipcRenderer } from '../lib/ipc' + +export const cleanerApi = { + runCleaner: (input: CleanerInput) => invokeIpc(IPC_CHANNELS.CLEANER_RUN, input), + exportResults: (items: ExportResultItem[]) => + invokeIpc(IPC_CHANNELS.CLEANER_EXPORT_RESULTS, items), + onProgress: (callback: (data: CleanerProgress) => void) => { + const subscription = (_event: Electron.IpcRendererEvent, data: CleanerProgress) => + callback(data) + ipcRenderer.on(IPC_CHANNELS.CLEANER_PROGRESS, subscription) + return () => ipcRenderer.removeListener(IPC_CHANNELS.CLEANER_PROGRESS, subscription) + } +} as const diff --git a/src/preload/api/database.ts b/src/preload/api/database.ts new file mode 100644 index 0000000..d65afb8 --- /dev/null +++ b/src/preload/api/database.ts @@ -0,0 +1,17 @@ +import type { MySqlConfig, SqlServerConfig } from '../../main/types/ipc-api.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc } from '../lib/ipc' + +export const databaseApi = { + connectMySql: (config: MySqlConfig) => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_CONNECT, config), + disconnectMySql: () => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_DISCONNECT), + isMySqlConnected: () => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_IS_CONNECTED), + queryMySql: (sql: string, params?: unknown[]) => + invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_QUERY, sql, params), + connectSqlServer: (config: SqlServerConfig) => + invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_CONNECT, config), + disconnectSqlServer: () => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_DISCONNECT), + isSqlServerConnected: () => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_IS_CONNECTED), + querySqlServer: (sql: string, params?: Record) => + invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_QUERY, sql, params) +} as const diff --git a/src/preload/api/extractor.ts b/src/preload/api/extractor.ts new file mode 100644 index 0000000..a370771 --- /dev/null +++ b/src/preload/api/extractor.ts @@ -0,0 +1,21 @@ +import type { ExtractorInput, ExtractionProgress } from '../../main/types/extractor.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc, ipcRenderer } from '../lib/ipc' + +export const extractorApi = { + runExtractor: (input: ExtractorInput) => invokeIpc(IPC_CHANNELS.EXTRACTOR_RUN, input), + onProgress: (callback: (data: ExtractionProgress) => void) => { + const subscription = (_event: Electron.IpcRendererEvent, data: ExtractionProgress) => + callback(data) + ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) + return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) + }, + onLog: (callback: (data: { level: string; message: string }) => void) => { + const subscription = ( + _event: Electron.IpcRendererEvent, + data: { level: string; message: string } + ) => callback(data) + ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_LOG, subscription) + return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_LOG, subscription) + } +} as const diff --git a/src/preload/api/file.ts b/src/preload/api/file.ts new file mode 100644 index 0000000..c9eb94d --- /dev/null +++ b/src/preload/api/file.ts @@ -0,0 +1,11 @@ +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc } from '../lib/ipc' + +export const fileApi = { + readFile: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_READ, filePath), + writeFile: (filePath: string, content: string) => + invokeIpc(IPC_CHANNELS.FILE_WRITE, filePath, content), + fileExists: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_EXISTS, filePath), + listFiles: (dirPath: string) => invokeIpc(IPC_CHANNELS.FILE_LIST, dirPath), + openPath: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_OPEN_PATH, filePath) +} as const diff --git a/src/preload/api/index.ts b/src/preload/api/index.ts new file mode 100644 index 0000000..e1c835a --- /dev/null +++ b/src/preload/api/index.ts @@ -0,0 +1,39 @@ +import { processApi } from './process' +import { fileApi } from './file' +import { extractorApi } from './extractor' +import { cleanerApi } from './cleaner' +import { resolverApi } from './resolver' +import { authApi } from './auth' +import { databaseApi } from './database' +import { validationApi } from './validation' +import { + configApi, + materialTypeApi, + materialsApi, + reportApi, + settingsApi, + updateApi, + userErpConfigApi +} from './materials' +import { loggerApi } from './logger' + +export const api = { + process: processApi, + file: fileApi, + extractor: extractorApi, + cleaner: cleanerApi, + resolver: resolverApi, + auth: authApi, + database: databaseApi, + validation: validationApi, + materials: materialsApi, + settings: settingsApi, + materialType: materialTypeApi, + userErpConfig: userErpConfigApi, + config: configApi, + logger: loggerApi, + report: reportApi, + update: updateApi +} as const + +export type ElectronApi = typeof api diff --git a/src/preload/api/logger.ts b/src/preload/api/logger.ts new file mode 100644 index 0000000..03ecd6d --- /dev/null +++ b/src/preload/api/logger.ts @@ -0,0 +1,14 @@ +import type { LogLevel } from '../../shared/ipc-channels' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { ipcRenderer } from '../lib/ipc' + +export const loggerApi = { + log: (level: LogLevel, message: string, context?: Record): void => { + ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, { + level, + message, + context, + timestamp: Date.now() + }) + } +} as const diff --git a/src/preload/api/materials.ts b/src/preload/api/materials.ts new file mode 100644 index 0000000..db0ca19 --- /dev/null +++ b/src/preload/api/materials.ts @@ -0,0 +1,91 @@ +import type { + MaterialTypeBatchRequest, + MaterialTypeRecord +} from '../../main/types/validation.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import type { CleanerConfig } from '../../main/types/config.schema' +import type { + DownloadReleaseRequest, + UpdateDialogCatalog, + UpdateStatus +} from '../../main/types/update.types' +import { invokeIpc, ipcRenderer } from '../lib/ipc' + +type ErpSettingsPayload = { + erp?: { + username?: string + password?: string + } +} + +export const materialsApi = { + upsertBatch: (materials: { materialCode: string; managerName: string }[]) => + invokeIpc(IPC_CHANNELS.MATERIALS_UPSERT_BATCH, { materials }), + delete: (materialCodes: string[]) => invokeIpc(IPC_CHANNELS.MATERIALS_DELETE, { materialCodes }), + getManagers: () => invokeIpc(IPC_CHANNELS.MATERIALS_GET_MANAGERS), + getByManager: (managerName: string) => + invokeIpc(IPC_CHANNELS.MATERIALS_GET_BY_MANAGER, managerName), + getAll: () => invokeIpc(IPC_CHANNELS.MATERIALS_GET_ALL), + getStatistics: () => invokeIpc(IPC_CHANNELS.MATERIALS_GET_STATISTICS), + updateManager: (materialCode: string, managerName: string) => + invokeIpc(IPC_CHANNELS.MATERIALS_UPDATE_MANAGER, { materialCode, managerName }) +} as const + +export const settingsApi = { + getUserType: () => invokeIpc(IPC_CHANNELS.SETTINGS_GET_USER_TYPE), + getSettings: () => invokeIpc(IPC_CHANNELS.SETTINGS_GET_SETTINGS), + saveSettings: (settings: ErpSettingsPayload) => + invokeIpc(IPC_CHANNELS.SETTINGS_SAVE_SETTINGS, settings), + resetDefaults: () => invokeIpc(IPC_CHANNELS.SETTINGS_RESET_DEFAULTS), + testDbConnection: () => invokeIpc(IPC_CHANNELS.SETTINGS_TEST_DB_CONNECTION) +} as const + +export const materialTypeApi = { + getAll: () => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_ALL), + getByManager: (managerName: string) => + invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_BY_MANAGER, managerName), + getManagers: () => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_MANAGERS), + upsert: (materialName: string, managerName: string) => + invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT, { materialName, managerName }), + delete: (materialName: string, managerName: string) => + invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_DELETE, { materialName, managerName }), + upsertBatch: (request: MaterialTypeBatchRequest) => + invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT_BATCH, request) +} as const + +export const userErpConfigApi = { + getCurrent: () => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_CURRENT), + update: (config: { url: string; username: string; password: string }) => + invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_UPDATE, config), + testConnection: (config: { url: string; username: string; password: string }) => + invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_TEST_CONNECTION, config), + getAll: () => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL) +} as const + +export const configApi = { + getCleaner: () => invokeIpc(IPC_CHANNELS.CONFIG_GET_CLEANER), + updateCleaner: (updates: Partial) => + invokeIpc(IPC_CHANNELS.CONFIG_UPDATE_CLEANER, updates) +} as const + +export const reportApi = { + listAll: () => invokeIpc(IPC_CHANNELS.REPORT_LIST_ALL), + listByUser: (username: string) => invokeIpc(IPC_CHANNELS.REPORT_LIST_BY_USER, username), + download: (key: string) => invokeIpc(IPC_CHANNELS.REPORT_DOWNLOAD, key) +} as const + +export const updateApi = { + getStatus: () => invokeIpc(IPC_CHANNELS.UPDATE_GET_STATUS), + checkNow: () => invokeIpc(IPC_CHANNELS.UPDATE_CHECK_NOW), + getCatalog: () => invokeIpc(IPC_CHANNELS.UPDATE_GET_CATALOG), + getChangelog: (release: DownloadReleaseRequest) => + invokeIpc(IPC_CHANNELS.UPDATE_GET_CHANGELOG, release), + downloadRelease: (release: DownloadReleaseRequest) => + invokeIpc(IPC_CHANNELS.UPDATE_DOWNLOAD_RELEASE, release), + installDownloaded: () => invokeIpc(IPC_CHANNELS.UPDATE_INSTALL_DOWNLOADED), + onStatusChanged: (callback: (data: UpdateStatus) => void) => { + const subscription = (_event: Electron.IpcRendererEvent, data: UpdateStatus) => callback(data) + ipcRenderer.on(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) + return () => ipcRenderer.removeListener(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) + } +} as const diff --git a/src/preload/api/process.ts b/src/preload/api/process.ts new file mode 100644 index 0000000..b7fa6fd --- /dev/null +++ b/src/preload/api/process.ts @@ -0,0 +1,7 @@ +export const processApi = { + versions: { + electron: process.versions.electron, + chrome: process.versions.chrome, + node: process.versions.node + } +} as const diff --git a/src/preload/api/resolver.ts b/src/preload/api/resolver.ts new file mode 100644 index 0000000..7555f1a --- /dev/null +++ b/src/preload/api/resolver.ts @@ -0,0 +1,8 @@ +import type { ResolverInput } from '../../main/types/resolver-ipc.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc } from '../lib/ipc' + +export const resolverApi = { + resolve: (input: ResolverInput) => invokeIpc(IPC_CHANNELS.RESOLVER_RESOLVE, input), + validateFormat: (inputs: string[]) => invokeIpc(IPC_CHANNELS.RESOLVER_VALIDATE_FORMAT, inputs) +} as const diff --git a/src/preload/api/validation.ts b/src/preload/api/validation.ts new file mode 100644 index 0000000..fda5b39 --- /dev/null +++ b/src/preload/api/validation.ts @@ -0,0 +1,12 @@ +import type { ValidationRequest } from '../../main/types/validation.types' +import { IPC_CHANNELS } from '../../shared/ipc-channels' +import { invokeIpc } from '../lib/ipc' + +export const validationApi = { + validate: (request: ValidationRequest) => invokeIpc(IPC_CHANNELS.VALIDATION_VALIDATE, request), + setSharedProductionIds: (productionIds: string[]) => + invokeIpc(IPC_CHANNELS.VALIDATION_SET_SHARED_PRODUCTION_IDS, productionIds), + getSharedProductionIds: () => invokeIpc(IPC_CHANNELS.VALIDATION_GET_SHARED_PRODUCTION_IDS), + clearSharedProductionIds: () => invokeIpc(IPC_CHANNELS.VALIDATION_CLEAR_SHARED_PRODUCTION_IDS), + getCleanerData: () => invokeIpc(IPC_CHANNELS.VALIDATION_GET_CLEANER_DATA) +} as const diff --git a/src/preload/index.ts b/src/preload/index.ts index e0bc5b3..026ac5e 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,245 +1,5 @@ -import { contextBridge, ipcRenderer } from 'electron' -import type { MySqlConfig, SqlServerConfig } from '../main/types/ipc-api.types' -import type { ExtractorInput, ExtractionProgress } from '../main/types/extractor.types' -import type { CleanerInput, CleanerProgress, ExportResultItem } from '../main/types/cleaner.types' -import type { ResolverInput } from '../main/types/resolver-ipc.types' -import type { LoginRequest } from '../main/types/auth-ipc.types' -import type { UserInfo } from '../main/types/user.types' -import type { - ValidationRequest, - MaterialTypeRecord, - MaterialTypeBatchRequest -} from '../main/types/validation.types' -import type { IpcResult } from '../main/types/ipc.types' -import { IPC_CHANNELS, type LogLevel } from '../shared/ipc-channels' -import type { CleanerConfig } from '../main/types/config.schema' -import type { DownloadReleaseRequest, UpdateStatus } from '../main/types/update.types' -import type { UpdateDialogCatalog } from '../main/types/update.types' - -type ErpSettingsPayload = { - erp?: { - username?: string - password?: string - } -} - -const processApi = { - versions: { - electron: process.versions.electron, - chrome: process.versions.chrome, - node: process.versions.node - } -} - -function invokeIpc(channel: string, ...args: unknown[]): Promise> { - return ipcRenderer.invoke(channel, ...args).then((result: unknown) => { - if (result && typeof result === 'object' && 'success' in (result as Record)) { - const typed = result as Record - const hasIpcShape = 'data' in typed || 'code' in typed || 'error' in typed - - if (hasIpcShape) { - return typed as unknown as IpcResult - } - - if (typed.success === true) { - return { success: true, data: result as T } - } - - return { - success: false, - error: typeof typed.error === 'string' ? typed.error : 'IPC operation failed' - } - } - - return { success: true, data: result as T } - }) -} - -const api = { - process: processApi, - - file: { - readFile: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_READ, filePath), - writeFile: (filePath: string, content: string) => - invokeIpc(IPC_CHANNELS.FILE_WRITE, filePath, content), - fileExists: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_EXISTS, filePath), - listFiles: (dirPath: string) => invokeIpc(IPC_CHANNELS.FILE_LIST, dirPath), - openPath: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_OPEN_PATH, filePath) - }, - - extractor: { - runExtractor: (input: ExtractorInput): Promise => - invokeIpc(IPC_CHANNELS.EXTRACTOR_RUN, input), - onProgress: (callback: (data: ExtractionProgress) => void) => { - const subscription = (_event: Electron.IpcRendererEvent, data: ExtractionProgress) => - callback(data) - ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) - return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) - }, - onLog: (callback: (data: { level: string; message: string }) => void) => { - const subscription = ( - _event: Electron.IpcRendererEvent, - data: { level: string; message: string } - ) => callback(data) - ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_LOG, subscription) - return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_LOG, subscription) - } - }, - - cleaner: { - runCleaner: (input: CleanerInput): Promise => - invokeIpc(IPC_CHANNELS.CLEANER_RUN, input), - exportResults: (items: ExportResultItem[]): Promise => - invokeIpc(IPC_CHANNELS.CLEANER_EXPORT_RESULTS, items), - onProgress: (callback: (data: CleanerProgress) => void) => { - const subscription = (_event: Electron.IpcRendererEvent, data: CleanerProgress) => - callback(data) - ipcRenderer.on(IPC_CHANNELS.CLEANER_PROGRESS, subscription) - return () => ipcRenderer.removeListener(IPC_CHANNELS.CLEANER_PROGRESS, subscription) - } - }, - - resolver: { - resolve: (input: ResolverInput): Promise => - invokeIpc(IPC_CHANNELS.RESOLVER_RESOLVE, input), - validateFormat: (inputs: string[]): Promise => - invokeIpc(IPC_CHANNELS.RESOLVER_VALIDATE_FORMAT, inputs) - }, - - auth: { - getComputerName: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_COMPUTER_NAME), - silentLogin: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_SILENT_LOGIN), - login: (request: LoginRequest): Promise => - invokeIpc(IPC_CHANNELS.AUTH_LOGIN, request), - logout: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_LOGOUT), - getCurrentUser: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_CURRENT_USER), - getAllUsers: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_ALL_USERS), - switchUser: (userInfo: UserInfo): Promise => - invokeIpc(IPC_CHANNELS.AUTH_SWITCH_USER, userInfo), - isAdmin: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_IS_ADMIN) - }, - - database: { - connectMySql: (config: MySqlConfig): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_CONNECT, config), - disconnectMySql: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_DISCONNECT), - isMySqlConnected: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_IS_CONNECTED), - queryMySql: (sql: string, params?: any[]): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_QUERY, sql, params), - connectSqlServer: (config: SqlServerConfig): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_CONNECT, config), - disconnectSqlServer: (): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_DISCONNECT), - isSqlServerConnected: (): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_IS_CONNECTED), - querySqlServer: (sql: string, params?: Record): Promise => - invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_QUERY, sql, params) - }, - - validation: { - validate: (request: ValidationRequest): Promise => - invokeIpc(IPC_CHANNELS.VALIDATION_VALIDATE, request), - setSharedProductionIds: (productionIds: string[]): Promise => - invokeIpc(IPC_CHANNELS.VALIDATION_SET_SHARED_PRODUCTION_IDS, productionIds), - getSharedProductionIds: (): Promise => - invokeIpc(IPC_CHANNELS.VALIDATION_GET_SHARED_PRODUCTION_IDS), - clearSharedProductionIds: (): Promise => - invokeIpc(IPC_CHANNELS.VALIDATION_CLEAR_SHARED_PRODUCTION_IDS), - getCleanerData: (): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_GET_CLEANER_DATA) - }, - - materials: { - upsertBatch: (materials: { materialCode: string; managerName: string }[]): Promise => - invokeIpc(IPC_CHANNELS.MATERIALS_UPSERT_BATCH, { materials }), - delete: (materialCodes: string[]): Promise => - invokeIpc(IPC_CHANNELS.MATERIALS_DELETE, { materialCodes }), - getManagers: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_MANAGERS), - getByManager: (managerName: string): Promise => - invokeIpc(IPC_CHANNELS.MATERIALS_GET_BY_MANAGER, managerName), - getAll: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_ALL), - getStatistics: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_STATISTICS), - updateManager: (materialCode: string, managerName: string): Promise => - invokeIpc(IPC_CHANNELS.MATERIALS_UPDATE_MANAGER, { materialCode, managerName }) - }, - - settings: { - getUserType: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_GET_USER_TYPE), - getSettings: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_GET_SETTINGS), - saveSettings: (settings: ErpSettingsPayload): Promise => - invokeIpc(IPC_CHANNELS.SETTINGS_SAVE_SETTINGS, settings), - resetDefaults: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_RESET_DEFAULTS), - testDbConnection: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_TEST_DB_CONNECTION) - }, - - materialType: { - getAll: (): Promise> => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_ALL), - getByManager: (managerName: string): Promise> => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_BY_MANAGER, managerName), - getManagers: (): Promise> => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_MANAGERS), - upsert: (materialName: string, managerName: string): Promise => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT, { materialName, managerName }), - delete: (materialName: string, managerName: string): Promise => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_DELETE, { materialName, managerName }), - upsertBatch: (request: MaterialTypeBatchRequest): Promise => - invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT_BATCH, request) - }, - - userErpConfig: { - getCurrent: (): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_CURRENT), - update: (config: { url: string; username: string; password: string }): Promise => - invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_UPDATE, config), - testConnection: (config: { - url: string - username: string - password: string - }): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_TEST_CONNECTION, config), - getAll: (): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL) - }, - - config: { - getCleaner: (): Promise> => invokeIpc(IPC_CHANNELS.CONFIG_GET_CLEANER), - updateCleaner: (updates: Partial): Promise> => - invokeIpc(IPC_CHANNELS.CONFIG_UPDATE_CLEANER, updates) - }, - - logger: { - log: (level: LogLevel, message: string, context?: Record): void => { - ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, { - level, - message, - context, - timestamp: Date.now() - }) - } - }, - - report: { - listAll: (): Promise => invokeIpc(IPC_CHANNELS.REPORT_LIST_ALL), - listByUser: (username: string): Promise => - invokeIpc(IPC_CHANNELS.REPORT_LIST_BY_USER, username), - download: (key: string): Promise => invokeIpc(IPC_CHANNELS.REPORT_DOWNLOAD, key) - }, - - update: { - getStatus: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_GET_STATUS), - checkNow: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_CHECK_NOW), - getCatalog: (): Promise> => - invokeIpc(IPC_CHANNELS.UPDATE_GET_CATALOG), - getChangelog: (release: DownloadReleaseRequest): Promise> => - invokeIpc(IPC_CHANNELS.UPDATE_GET_CHANGELOG, release), - downloadRelease: (release: DownloadReleaseRequest): Promise> => - invokeIpc(IPC_CHANNELS.UPDATE_DOWNLOAD_RELEASE, release), - installDownloaded: (): Promise> => - invokeIpc(IPC_CHANNELS.UPDATE_INSTALL_DOWNLOADED), - onStatusChanged: (callback: (data: UpdateStatus) => void) => { - const subscription = (_event: Electron.IpcRendererEvent, data: UpdateStatus) => callback(data) - ipcRenderer.on(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) - return () => ipcRenderer.removeListener(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) - } - } -} as const +import { contextBridge } from 'electron' +import { api } from './api' if (process.contextIsolated) { try { diff --git a/src/preload/lib/ipc.ts b/src/preload/lib/ipc.ts new file mode 100644 index 0000000..e5e199a --- /dev/null +++ b/src/preload/lib/ipc.ts @@ -0,0 +1,28 @@ +import { ipcRenderer } from 'electron' +import type { IpcResult } from '../../main/types/ipc.types' + +export function invokeIpc(channel: string, ...args: unknown[]): Promise> { + return ipcRenderer.invoke(channel, ...args).then((result: unknown) => { + if (result && typeof result === 'object' && 'success' in (result as Record)) { + const typed = result as Record + const hasIpcShape = 'data' in typed || 'code' in typed || 'error' in typed + + if (hasIpcShape) { + return typed as unknown as IpcResult + } + + if (typed.success === true) { + return { success: true, data: result as T } + } + + return { + success: false, + error: typeof typed.error === 'string' ? typed.error : 'IPC operation failed' + } + } + + return { success: true, data: result as T } + }) +} + +export { ipcRenderer }