返回 oh-my-ppt
preview-handlers.ts
根目录 / src / main / session / preview-handlers.ts
1 import { ipcMain } from 'electron'
2 import path from 'path'
3 import fs from 'fs'
4 import { extractPagesDataFromIndex } from './template-builder'
5 import type { IpcContext } from '../ipc/context'
6
7 export function registerPreviewHandlers(ctx: IpcContext): void {
8 const { parsePathPayload, normalizeSessionId, assertPathInAllowedRoots } = ctx
9
10 ipcMain.handle('preview:load', async (_event, payload, legacySessionId?: string) => {
11 const parsed = parsePathPayload(payload, 'htmlPath')
12 const sessionId = parsed.sessionId ?? normalizeSessionId(legacySessionId)
13 const safeHtmlPath = await assertPathInAllowedRoots({
14 filePath: parsed.filePath,
15 mode: 'read',
16 sessionId,
17 htmlOnly: true
18 })
19 return fs.promises.readFile(safeHtmlPath, 'utf-8')
20 })
21
22 ipcMain.handle(
23 'preview:loadPage',
24 async (_event, payloadOrHtmlPath: unknown, legacyPageId?: string, legacySessionId?: string) => {
25 let htmlPath = ''
26 let pageId = ''
27 let sessionId: string | undefined
28 if (payloadOrHtmlPath && typeof payloadOrHtmlPath === 'object') {
29 const payload = payloadOrHtmlPath as {
30 htmlPath?: unknown
31 path?: unknown
32 pageId?: unknown
33 sessionId?: unknown
34 }
35 htmlPath =
36 typeof payload.htmlPath === 'string'
37 ? payload.htmlPath
38 : typeof payload.path === 'string'
39 ? payload.path
40 : ''
41 pageId = typeof payload.pageId === 'string' ? payload.pageId : ''
42 sessionId = normalizeSessionId(payload.sessionId)
43 } else {
44 htmlPath = typeof payloadOrHtmlPath === 'string' ? payloadOrHtmlPath : ''
45 pageId = typeof legacyPageId === 'string' ? legacyPageId : ''
46 sessionId = normalizeSessionId(legacySessionId)
47 }
48 const normalizedPageId = pageId.trim()
49 if (!normalizedPageId) {
50 throw new Error('pageId 不能为空')
51 }
52 const safeHtmlPath = await assertPathInAllowedRoots({
53 filePath: htmlPath,
54 mode: 'read',
55 sessionId,
56 htmlOnly: true
57 })
58 const isPageFile = path.basename(safeHtmlPath).toLowerCase() !== 'index.html'
59 if (isPageFile) {
60 const html = await fs.promises.readFile(safeHtmlPath, 'utf-8')
61 const numberMatch = safeHtmlPath.match(/page-(\d+)\.html?$/i)
62 const pageNumber = numberMatch ? Number(numberMatch[1]) : 1
63 return {
64 pageNumber,
65 pageId: normalizedPageId,
66 title: `Page ${pageNumber}`,
67 html
68 }
69 }
70
71 const indexHtml = await fs.promises.readFile(safeHtmlPath, 'utf-8')
72 const pages = extractPagesDataFromIndex(indexHtml)
73 const page = pages.find((p) => p.id === normalizedPageId || p.pageId === normalizedPageId)
74 if (!page) throw new Error(`Page ${normalizedPageId} not found in ${safeHtmlPath}`)
75 if (page.htmlPath) {
76 const resolvedPagePath = path.resolve(path.dirname(safeHtmlPath), page.htmlPath)
77 const safeResolvedPagePath = await assertPathInAllowedRoots({
78 filePath: resolvedPagePath,
79 mode: 'read',
80 sessionId,
81 htmlOnly: true
82 })
83 const html = await fs.promises.readFile(safeResolvedPagePath, 'utf-8')
84 return {
85 pageNumber: page.pageNumber,
86 pageId: page.pageId,
87 title: page.title,
88 html
89 }
90 }
91 return page
92 }
93 )
94 }
95
95 lines TYPESCRIPT