Files
BIPMaterialManager/src/main/ipc/file-handler.ts
test c4fff84848 style: format code with Prettier
Apply Prettier formatting to maintain consistent code style across the codebase.
Changes include formatting improvements for:
- IPC handlers (database, file, material-type, resolver, user-erp-config, validation)
- Type definitions (ipc-api.types)
- React components (MaterialTypeManagementDialog)
- React hooks (useAuth, useCleaner, useValidation)
- Pages (SettingsPage)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-08 15:17:40 +08:00

100 lines
3.2 KiB
TypeScript

import { app, ipcMain, shell } from 'electron'
import * as fs from 'fs/promises'
import * as path from 'path'
import { createLogger } from '../services/logger'
import { IPC_CHANNELS } from '../../shared/ipc-channels'
import { ValidationError } from '../types/errors'
import { withErrorHandling, type IpcResult } from './index'
const log = createLogger('FileHandler')
function getAllowedRoots(): string[] {
return [path.resolve(app.getAppPath()), path.resolve(app.getPath('userData'))]
}
export function isPathWithinAllowedRoots(inputPath: string, roots: string[]): boolean {
const normalized = path.resolve(inputPath)
return roots.some((root) => {
const rel = path.relative(root, normalized)
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))
})
}
function normalizeAndValidatePath(inputPath: string): string {
const normalized = path.resolve(inputPath)
const isAllowed = isPathWithinAllowedRoots(normalized, getAllowedRoots())
if (!isAllowed) {
throw new ValidationError('Path is outside allowed roots', 'VAL_INVALID_INPUT')
}
return normalized
}
export function registerFileHandlers(): void {
ipcMain.handle(
IPC_CHANNELS.FILE_READ,
async (_event, filePath: string): Promise<IpcResult<string>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
log.debug('Reading file', { filePath: safePath })
return await fs.readFile(safePath, 'utf-8')
}, 'file:read')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_WRITE,
async (_event, filePath: string, content: string): Promise<IpcResult<void>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
log.debug('Writing file', { filePath: safePath })
const dir = path.dirname(safePath)
await fs.mkdir(dir, { recursive: true })
await fs.writeFile(safePath, content, 'utf-8')
}, 'file:write')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_EXISTS,
async (_event, filePath: string): Promise<IpcResult<boolean>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
try {
await fs.access(safePath)
return true
} catch {
return false
}
}, 'file:exists')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_LIST,
async (_event, dirPath: string): Promise<IpcResult<string[]>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(dirPath)
log.debug('Listing directory', { dirPath: safePath })
const entries = await fs.readdir(safePath, { withFileTypes: true })
return entries
.filter((entry) => entry.isFile())
.map((entry) => entry.name)
.sort()
}, 'file:list')
}
)
ipcMain.handle(
IPC_CHANNELS.FILE_OPEN_PATH,
async (_event, filePath: string): Promise<IpcResult<void>> => {
return withErrorHandling(async () => {
const safePath = normalizeAndValidatePath(filePath)
log.debug('Opening path in explorer', { filePath: safePath })
await fs.access(safePath)
await shell.openPath(safePath)
}, 'file:openPath')
}
)
}