Files
PromptX/src/lib/core/resource/protocols/UserProtocol.js

308 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const ResourceProtocol = require('./ResourceProtocol')
const path = require('path')
const fs = require('fs').promises
// 延迟加载platform-folders以处理可能的原生模块依赖
let platformFolders = null
const getPlatformFolders = () => {
if (!platformFolders) {
try {
platformFolders = require('platform-folders')
} catch (error) {
// 如果platform-folders不可用回退到os.homedir()
const os = require('os')
platformFolders = {
getHomeFolder: () => os.homedir(),
getDesktopFolder: () => path.join(os.homedir(), 'Desktop'),
getDocumentsFolder: () => path.join(os.homedir(), 'Documents'),
getDownloadsFolder: () => path.join(os.homedir(), 'Downloads'),
getMusicFolder: () => path.join(os.homedir(), 'Music'),
getPicturesFolder: () => path.join(os.homedir(), 'Pictures'),
getVideosFolder: () => path.join(os.homedir(), 'Videos')
}
console.warn('platform-folders不可用使用os.homedir()回退方案')
}
}
return platformFolders
}
/**
* 用户目录协议实现
* 实现@user://协议用于访问用户的标准目录Documents、Desktop、Downloads等
*/
class UserProtocol extends ResourceProtocol {
constructor (options = {}) {
super('user', options)
// 支持的用户目录映射
this.userDirs = {
home: 'getHomeFolder',
desktop: 'getDesktopFolder',
documents: 'getDocumentsFolder',
downloads: 'getDownloadsFolder',
music: 'getMusicFolder',
pictures: 'getPicturesFolder',
videos: 'getVideosFolder'
}
// 目录路径缓存
this.dirCache = new Map()
}
/**
* 设置注册表(保持与其他协议的一致性)
*/
setRegistry (registry) {
// User协议不使用注册表但为了一致性提供此方法
this.registry = registry || {}
}
/**
* 获取协议信息
* @returns {object} 协议信息
*/
getProtocolInfo () {
return {
name: 'user',
description: '用户目录协议,提供跨平台的用户标准目录访问',
location: 'user://{directory}/{path}',
examples: [
'user://documents/notes.txt',
'user://desktop/readme.md',
'user://downloads/',
'user://home/.bashrc'
],
supportedDirectories: Object.keys(this.userDirs),
params: this.getSupportedParams()
}
}
/**
* 支持的查询参数
* @returns {object} 参数说明
*/
getSupportedParams () {
return {
...super.getSupportedParams(),
exists: 'boolean - 仅返回存在的文件/目录',
type: 'string - 过滤类型 (file|dir|both)'
}
}
/**
* 验证用户协议路径
* @param {string} resourcePath - 资源路径
* @returns {boolean} 是否有效
*/
validatePath (resourcePath) {
if (!super.validatePath(resourcePath)) {
return false
}
// 解析路径的第一部分(目录类型)
const parts = resourcePath.split('/')
const dirType = parts[0]
return this.userDirs.hasOwnProperty(dirType)
}
/**
* 解析用户目录路径
* @param {string} resourcePath - 原始资源路径,如 "documents/notes.txt"
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolvePath (resourcePath, queryParams) {
const parts = resourcePath.split('/')
const dirType = parts[0]
const relativePath = parts.slice(1).join('/')
// 验证目录类型
if (!this.userDirs[dirType]) {
throw new Error(`不支持的用户目录类型: ${dirType}。支持的类型: ${Object.keys(this.userDirs).join(', ')}`)
}
// 获取用户目录路径
const userDirPath = await this.getUserDirectory(dirType)
// 如果没有相对路径,返回目录本身
if (!relativePath) {
return userDirPath
}
// 拼接完整路径
const fullPath = path.join(userDirPath, relativePath)
// 安全检查:确保路径在用户目录内
const resolvedPath = path.resolve(fullPath)
const resolvedUserDir = path.resolve(userDirPath)
if (!resolvedPath.startsWith(resolvedUserDir)) {
throw new Error(`安全错误:路径超出用户目录范围: ${resolvedPath}`)
}
return resolvedPath
}
/**
* 获取用户目录路径
* @param {string} dirType - 目录类型
* @returns {Promise<string>} 目录路径
*/
async getUserDirectory (dirType) {
// 检查缓存
if (this.dirCache.has(dirType)) {
return this.dirCache.get(dirType)
}
const folders = getPlatformFolders()
const methodName = this.userDirs[dirType]
if (!folders[methodName]) {
throw new Error(`未找到用户目录获取方法: ${methodName}`)
}
try {
let dirPath
// 调用platform-folders方法
if (typeof folders[methodName] === 'function') {
dirPath = await folders[methodName]()
} else {
dirPath = folders[methodName]
}
// 缓存结果
this.dirCache.set(dirType, dirPath)
return dirPath
} catch (error) {
throw new Error(`获取用户目录失败 (${dirType}): ${error.message}`)
}
}
/**
* 加载资源内容
* @param {string} resolvedPath - 解析后的路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 资源内容
*/
async loadContent (resolvedPath, queryParams) {
try {
// 检查路径是否存在
const stats = await fs.stat(resolvedPath)
if (stats.isDirectory()) {
return await this.loadDirectoryContent(resolvedPath, queryParams)
} else if (stats.isFile()) {
return await this.loadFileContent(resolvedPath, queryParams)
} else {
throw new Error(`不支持的文件类型: ${resolvedPath}`)
}
} catch (error) {
if (error.code === 'ENOENT') {
// 如果设置了exists参数为false返回空内容而不是错误
if (queryParams && queryParams.get('exists') === 'false') {
return ''
}
throw new Error(`文件或目录不存在: ${resolvedPath}`)
}
throw error
}
}
/**
* 加载文件内容
* @param {string} filePath - 文件路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 文件内容
*/
async loadFileContent (filePath, queryParams) {
const encoding = queryParams?.get('encoding') || 'utf8'
return await fs.readFile(filePath, encoding)
}
/**
* 加载目录内容
* @param {string} dirPath - 目录路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 目录内容列表
*/
async loadDirectoryContent (dirPath, queryParams) {
const entries = await fs.readdir(dirPath, { withFileTypes: true })
// 应用类型过滤
const typeFilter = queryParams?.get('type')
let filteredEntries = entries
if (typeFilter) {
filteredEntries = entries.filter(entry => {
switch (typeFilter) {
case 'file': return entry.isFile()
case 'dir': return entry.isDirectory()
case 'both': return true
default: return true
}
})
}
// 格式化输出
const format = queryParams?.get('format') || 'list'
switch (format) {
case 'json':
return JSON.stringify(
filteredEntries.map(entry => ({
name: entry.name,
type: entry.isDirectory() ? 'directory' : 'file',
path: path.join(dirPath, entry.name)
})),
null,
2
)
case 'paths':
return filteredEntries
.map(entry => path.join(dirPath, entry.name))
.join('\n')
case 'list':
default:
return filteredEntries
.map(entry => {
const type = entry.isDirectory() ? '[DIR]' : '[FILE]'
return `${type} ${entry.name}`
})
.join('\n')
}
}
/**
* 列出所有支持的用户目录
* @returns {Promise<object>} 目录信息
*/
async listUserDirectories () {
const result = {}
for (const dirType of Object.keys(this.userDirs)) {
try {
result[dirType] = await this.getUserDirectory(dirType)
} catch (error) {
result[dirType] = { error: error.message }
}
}
return result
}
/**
* 清除目录缓存
*/
clearCache () {
super.clearCache()
this.dirCache.clear()
}
}
module.exports = UserProtocol