feat(remote): 支持浏览系统磁盘目录
- 添加 getDrives() 方法获取磁盘驱动器列表 - 修改 browseDirectory() 支持 allowSystem 参数浏览系统路径 - 添加 /api/files/drives 路由 - 修改前端 RemoteFilePanel 支持显示驱动器和系统目录浏览
This commit is contained in:
@@ -17,14 +17,25 @@ router.get('/', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/drives', (req, res) => {
|
||||
try {
|
||||
const drives = fileService.getDrives();
|
||||
res.json({ items: drives });
|
||||
} catch (error) {
|
||||
logger.error('Failed to get drives', { error: error.message });
|
||||
res.status(500).json({ error: 'Failed to get drives' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/browse', (req, res) => {
|
||||
try {
|
||||
const path = req.query.path || '';
|
||||
const result = fileService.browseDirectory(path);
|
||||
const filePath = req.query.path || '';
|
||||
const allowSystem = req.query.allowSystem === 'true';
|
||||
const result = fileService.browseDirectory(filePath, allowSystem);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
logger.error('Failed to browse directory', { error: error.message });
|
||||
res.status(500).json({ error: 'Failed to browse directory' });
|
||||
logger.error('Failed to browse directory', { error: error.message, stack: error.stack });
|
||||
res.status(500).json({ error: error.message || 'Failed to browse directory' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -136,48 +136,78 @@ class FileService {
|
||||
}
|
||||
}
|
||||
|
||||
browseDirectory(relativePath = '') {
|
||||
try {
|
||||
getDrives() {
|
||||
const drives = [];
|
||||
const letters = 'CDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
for (const letter of letters) {
|
||||
const drivePath = `${letter}:\\`;
|
||||
try {
|
||||
fs.accessSync(drivePath);
|
||||
drives.push({ name: `${letter}:`, isDirectory: true, size: 0 });
|
||||
} catch {}
|
||||
}
|
||||
return drives;
|
||||
}
|
||||
|
||||
browseDirectory(relativePath = '', allowSystem = false) {
|
||||
let targetDir;
|
||||
let currentPath;
|
||||
|
||||
if (allowSystem) {
|
||||
currentPath = path.normalize(relativePath || '').replace(/^(\.\.(\/|\\|$))+/, '');
|
||||
if (!currentPath) {
|
||||
currentPath = '';
|
||||
}
|
||||
targetDir = currentPath || 'C:\\';
|
||||
} else {
|
||||
const safePath = path.normalize(relativePath || '').replace(/^(\.\.(\/|\\|$))+/, '');
|
||||
const targetDir = path.join(this.uploadDir, safePath);
|
||||
|
||||
targetDir = path.join(this.uploadDir, safePath);
|
||||
currentPath = safePath;
|
||||
|
||||
if (!targetDir.startsWith(this.uploadDir)) {
|
||||
return { error: 'Access denied', items: [], currentPath: '' };
|
||||
}
|
||||
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
return { error: 'Directory not found', items: [], currentPath: safePath };
|
||||
}
|
||||
|
||||
const items = fs.readdirSync(targetDir).map(name => {
|
||||
const itemPath = path.join(targetDir, name);
|
||||
const stat = fs.statSync(itemPath);
|
||||
const isDirectory = stat.isDirectory();
|
||||
|
||||
return {
|
||||
name,
|
||||
isDirectory,
|
||||
size: isDirectory ? 0 : stat.size,
|
||||
modified: stat.mtime,
|
||||
type: isDirectory ? 'directory' : path.extname(name)
|
||||
};
|
||||
});
|
||||
|
||||
items.sort((a, b) => {
|
||||
if (a.isDirectory && !b.isDirectory) return -1;
|
||||
if (!a.isDirectory && b.isDirectory) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
return {
|
||||
items,
|
||||
currentPath: safePath,
|
||||
parentPath: safePath ? path.dirname(safePath) : null
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to browse directory', { error: error.message });
|
||||
return { error: error.message, items: [], currentPath: relativePath };
|
||||
}
|
||||
|
||||
const items = [];
|
||||
|
||||
try {
|
||||
const files = fs.readdirSync(targetDir);
|
||||
|
||||
for (const name of files) {
|
||||
try {
|
||||
const itemPath = path.join(targetDir, name);
|
||||
const stat = fs.statSync(itemPath);
|
||||
const isDirectory = stat.isDirectory();
|
||||
items.push({
|
||||
name,
|
||||
isDirectory,
|
||||
size: isDirectory ? 0 : stat.size,
|
||||
modified: stat.mtime,
|
||||
type: isDirectory ? 'directory' : path.extname(name)
|
||||
});
|
||||
} catch (err) {
|
||||
logger.debug('Skipped inaccessible file', { name, error: err.message });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn('Failed to read directory', { targetDir, error: err.message });
|
||||
return { items: [], currentPath: currentPath, parentPath: path.dirname(currentPath) || null };
|
||||
}
|
||||
|
||||
items.sort((a, b) => {
|
||||
if (a.isDirectory && !b.isDirectory) return -1;
|
||||
if (!a.isDirectory && b.isDirectory) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
const parentPath = currentPath ? path.dirname(currentPath) : null;
|
||||
|
||||
return {
|
||||
items,
|
||||
currentPath: currentPath,
|
||||
parentPath: parentPath === currentPath ? null : parentPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user