新增:SFTP直连+连接池+autoConnect+文件服务器端口自动回退
- SFTP模块:连接/断开/文件CRUD/系统信息采集/base64二进制写入 - 连接池:多服务器同时在线,瞬间切换profile - autoConnect:启动时自动连接所有非本地服务器 - 端口自动回退:listenWithFallback消除TOCTOU,解决端口冲突崩溃 - 文件服务器URL集中管理:file-server.ts消除8+处硬编码端口 - Sidebar设置面板:添加服务器/自动连接/自动刷新开关 - 修复:validateFilePath越界panic、正则预编译 - 修复:注释准确性(RemoveAll/端口8073/动态端口文档)
This commit is contained in:
@@ -8,6 +8,8 @@ import { FILE_EXTENSIONS, FILE_SIZE_THRESHOLDS } from '@/utils/constants'
|
||||
import { normalizeFilePath, getExt } from '@/utils/fileUtils'
|
||||
import { detectFileTypeByContent } from '@/api/system'
|
||||
import { connectionManager } from '@/api/connection-manager'
|
||||
import { SftpTransport } from '@/api/sftp-transport'
|
||||
import { getFileServerBaseURL } from '@/api/file-server'
|
||||
import {
|
||||
isImageFile, isVideoFile, isAudioFile, isPdfFile,
|
||||
isHtmlFile, isMarkdownFile, isPreviewable as isPreviewableType,
|
||||
@@ -28,7 +30,7 @@ export interface UseFilePreviewOptions {
|
||||
}
|
||||
|
||||
function getLocalServerURL(): string {
|
||||
return 'http://localhost:8073'
|
||||
return getFileServerBaseURL()
|
||||
}
|
||||
|
||||
function resolveFileServerBase(): string {
|
||||
@@ -51,16 +53,28 @@ export function useFilePreview(options: UseFilePreviewOptions = {}) {
|
||||
const currentImageDimensions = ref('')
|
||||
|
||||
/**
|
||||
* 获取预览 URL(本地/远程自适应,每次实时计算)
|
||||
* 本地: http://localhost:8073/localfs/{encoded_path}
|
||||
* 远程: {baseUrl}/api/v1/proxy/localfs/{raw_path}(Cookie 自动携带认证)
|
||||
* 获取预览 URL(本地/远程/SFTP 自适应,每次实时计算)
|
||||
* 本地: {fileServerBaseURL}/localfs/{encoded_path}
|
||||
* 远程(HTTP): {baseUrl}/api/v1/proxy/localfs/{raw_path}
|
||||
* SFTP: 下载到本地临时目录 → {fileServerBaseURL}/localfs/{temp_path}
|
||||
*/
|
||||
const getPreviewUrl = (path: string): string => {
|
||||
if (!path) return ''
|
||||
const isSftp = connectionManager.isSftp()
|
||||
const isRemote = connectionManager.isRemote()
|
||||
|
||||
// SFTP 模式:需要先下载到本地临时目录
|
||||
// 注意:这里返回的是同步路径,实际下载在 updatePreviewUrl 中异步完成
|
||||
// 对于 SFTP 模式,getPreviewUrl 返回的 URL 会在 updatePreviewUrl 中被覆盖为临时文件路径
|
||||
if (isSftp) {
|
||||
const base = getLocalServerURL()
|
||||
let normalized = normalizeFilePath(path, true)
|
||||
const sep = base.endsWith('/') ? '' : '/'
|
||||
return `${base}${sep}localfs/${normalized}`
|
||||
}
|
||||
|
||||
const base = resolveFileServerBase()
|
||||
let normalized = normalizeFilePath(path, true)
|
||||
// 远程模式去掉前导 /,避免与 URL 基础路径拼接产生双斜杠(导致 307 重定向)
|
||||
if (isRemote && normalized.startsWith('/')) normalized = normalized.slice(1)
|
||||
const sep = base.endsWith('/') ? '' : '/'
|
||||
return `${base}${sep}${isRemote ? '' : 'localfs/'}${normalized}`
|
||||
@@ -92,9 +106,25 @@ export function useFilePreview(options: UseFilePreviewOptions = {}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新预览 URL
|
||||
* 更新预览 URL(SFTP 模式会先下载到本地临时目录)
|
||||
*/
|
||||
const updatePreviewUrl = async (path: string) => {
|
||||
if (!path) { previewUrl.value = ''; return }
|
||||
|
||||
// SFTP 模式:下载到本地临时目录后用本地文件服务器预览
|
||||
if (connectionManager.isSftp()) {
|
||||
const transport = connectionManager.getTransport()
|
||||
if (transport instanceof SftpTransport) {
|
||||
try {
|
||||
const tempPath = await transport.downloadForPreview(path)
|
||||
previewUrl.value = getPreviewUrl(tempPath)
|
||||
return
|
||||
} catch {
|
||||
// 下载失败,回退显示原始路径(会无法预览但不会崩溃)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previewUrl.value = getPreviewUrl(path)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user