feat(private_clipboard): 新增私有剪贴板文件访问接口并调整url返回逻辑

不再直接暴露本地文件路径作为文件url,改用api接口代理访问;新增对应路由处理文件的预览和下载请求,完善响应头配置。
This commit is contained in:
yangxiangyuan
2026-08-02 21:00:43 +08:00
parent 18e8e787d2
commit af5d0f43a5
+25 -2
View File
@@ -101,8 +101,7 @@ const saveFile = (raw, name, mime) => {
} }
const formatRow = (row) => { const formatRow = (row) => {
const filePath = row.file_path ? String(row.file_path) : '' const url = row && row.id && row.file_path ? `/api/private_clipboard/file/${row.id}` : ''
const url = filePath ? '/' + filePath.replace(/^\/+/, '') : ''
return { return {
id: row.id, id: row.id,
type: row.type, type: row.type,
@@ -402,6 +401,30 @@ const createRouter = () => {
// ===== 以下业务路由需要登录 ===== // ===== 以下业务路由需要登录 =====
router.use(mustAuth) router.use(mustAuth)
router.get('/file/:id', (req, res) => {
try {
const id = Number.parseInt(String(req.params.id || ''), 10)
if (!id) return res.status(400).json({ ok: false, error: 'invalid_id' })
const row = getDb().prepare('SELECT id, type, file_name, file_mime, file_path FROM clipboard_items WHERE id = ?').get(id)
if (!row || !row.file_path) return res.status(404).json({ ok: false, error: 'file_not_found' })
const rel = String(row.file_path || '').replace(/^\/+/, '')
const abs = path.join(process.cwd(), rel)
if (!fs.existsSync(abs)) return res.status(404).json({ ok: false, error: 'file_not_found' })
const mime = String(row.file_mime || '').trim() || 'application/octet-stream'
res.setHeader('Content-Type', mime)
res.setHeader('Cache-Control', 'private, no-store, max-age=0')
const safeName = normalizeName(row.file_name || 'file')
const isImage = String(row.type || '').toLowerCase() === 'image' || mime.toLowerCase().startsWith('image/')
const dispositionType = isImage ? 'inline' : 'attachment'
const fallbackName = safeName.replace(/[^\x20-\x7E]/g, '_')
const utf8Name = encodeURIComponent(safeName)
res.setHeader('Content-Disposition', `${dispositionType}; filename="${fallbackName}"; filename*=UTF-8''${utf8Name}`)
return res.sendFile(abs)
} catch (e) {
return res.status(500).json({ ok: false, error: String(e.message || e) })
}
})
router.get('/list', (req, res) => { router.get('/list', (req, res) => {
try { try {
const pageSizeRaw = String((req.query && req.query.page_size) || '50').trim().toLowerCase() const pageSizeRaw = String((req.query && req.query.page_size) || '50').trim().toLowerCase()