返回 oh-my-ppt
template-paths.ts
根目录 / src / main / templates / template-paths.ts
1 import { app } from 'electron'
2 import fs from 'fs'
3 import path from 'path'
4 import { customAlphabet } from 'nanoid'
5 import { allowLocalAssetRoot } from '../io/local-asset-roots'
6
7 const TEMPLATE_ID_RE = /^tpl_[a-zA-Z0-9_-]{8,80}$/
8 const nanoidLower = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 12)
9
10 export function createLowercaseId(): string {
11 return nanoidLower()
12 }
13
14 export function resolveTemplatesRoot(): string {
15 const root = path.join(app.getPath('userData'), 'templates')
16 allowLocalAssetRoot(root)
17 return root
18 }
19
20 export async function ensureTemplatesRoot(): Promise<string> {
21 const root = resolveTemplatesRoot()
22 await fs.promises.mkdir(root, { recursive: true })
23 allowLocalAssetRoot(root)
24 return root
25 }
26
27 export function isPathInside(targetPath: string, rootPath: string): boolean {
28 const relative = path.relative(rootPath, targetPath)
29 return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative))
30 }
31
32 export function normalizeTemplateId(value: unknown): string {
33 const id = typeof value === 'string' ? value.trim() : ''
34 if (!TEMPLATE_ID_RE.test(id)) throw new Error('模板 ID 无效')
35 return id
36 }
37
38 export function resolveTemplateDir(templatesRoot: string, templateId: string): string {
39 const id = normalizeTemplateId(templateId)
40 const dir = path.resolve(templatesRoot, id)
41 if (!isPathInside(dir, templatesRoot)) throw new Error('模板路径越界')
42 return dir
43 }
44
45 export function resolveTemplateManifestPath(templatesRoot: string, templateId: string): string {
46 return path.join(resolveTemplateDir(templatesRoot, templateId), 'manifest.json')
47 }
48
49 export function resolveTemplateRelativePath(templateDir: string, relativePath?: string | null): string | null {
50 const raw = typeof relativePath === 'string' ? relativePath.trim() : ''
51 if (!raw) return null
52 const resolved = path.resolve(templateDir, raw)
53 if (!isPathInside(resolved, templateDir)) return null
54 return resolved
55 }
56
57 export function createTemplateId(): string {
58 return `tpl_${createLowercaseId()}`
59 }
60
60 lines TYPESCRIPT