clouddisk-project/backend/src/routes/share.js
Team 6c144e81a3 团队工作进展:
- 后端: 分享功能 API (创建/验证/下载分享链接)
- 前端: 分享弹窗组件
- 数据库: 同步状态表
2026-03-10 07:36:48 +00:00

143 lines
3.6 KiB
JavaScript

const express = require('express');
const crypto = require('crypto');
const db = require('../db');
const router = express.Router();
// Create share link
router.post('/', (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'No token' });
try {
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'clouddisk-secret-key');
const { fileId, password, expiresIn } = req.body;
// Verify file ownership
const file = db.query(
'SELECT * FROM files WHERE id = ? AND user_id = ?',
[fileId, decoded.userId]
);
if (file.length === 0) {
return res.status(404).json({ error: 'File not found' });
}
// Generate share token
const shareToken = crypto.randomBytes(16).toString('hex');
// Calculate expiration
const expiresAt = expiresIn
? new Date(Date.now() + expiresIn * 1000).toISOString()
: null;
const result = db.run(
'INSERT INTO shares (file_id, share_token, password, expires_at) VALUES (?, ?, ?, ?)',
[fileId, shareToken, password || null, expiresAt]
);
res.json({
success: true,
shareToken,
shareUrl: `/share/${shareToken}`
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get share info
router.get('/:token', (req, res) => {
try {
const share = db.query(
'SELECT * FROM shares WHERE share_token = ?',
[req.params.token]
);
if (share.length === 0) {
return res.status(404).json({ error: 'Share not found' });
}
// Check expiration
if (share[0].expires_at && new Date(share[0].expires_at) < new Date()) {
return res.status(410).json({ error: 'Share expired' });
}
// Get file info
const file = db.query(
'SELECT id, name, type, size FROM files WHERE id = ?',
[share[0].file_id]
);
// Increment view count
db.run(
'UPDATE shares SET view_count = view_count + 1 WHERE id = ?',
[share[0].id]
);
res.json({
file: file[0],
requiresPassword: !!share[0].password,
expiresAt: share[0].expires_at,
viewCount: share[0].view_count
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Verify share password
router.post('/:token/verify', (req, res) => {
const { password } = req.body;
const share = db.query(
'SELECT * FROM shares WHERE share_token = ?',
[req.params.token]
);
if (share.length === 0) {
return res.status(404).json({ error: 'Share not found' });
}
if (share[0].password && share[0].password !== password) {
return res.status(401).json({ error: 'Invalid password' });
}
res.json({ success: true });
});
// Download shared file
router.get('/:token/download', (req, res) => {
const share = db.query(
'SELECT * FROM shares WHERE share_token = ?',
[req.params.token]
);
if (share.length === 0) {
return res.status(404).json({ error: 'Share not found' });
}
// Check if password protected
if (share[0].password) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Password required' });
}
}
const file = db.query(
'SELECT * FROM files WHERE id = ?',
[share[0].file_id]
);
if (file.length === 0) {
return res.status(404).json({ error: 'File not found' });
}
res.download(file[0].path, file[0].name);
});
module.exports = router;