团队工作进展:

- 后端: 分享功能 API (创建/验证/下载分享链接)
- 前端: 分享弹窗组件
- 数据库: 同步状态表
This commit is contained in:
Team 2026-03-10 07:36:48 +00:00
parent 61117803e1
commit 6c144e81a3
3 changed files with 157 additions and 0 deletions

View File

@ -57,3 +57,16 @@ db.exec(`
`);
module.exports = db;
// Sync tables (for reference - would be in migration in production)
db.exec(`
CREATE TABLE IF NOT EXISTS sync_status (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
last_sync_time DATETIME,
status TEXT DEFAULT 'idle',
total_files INTEGER DEFAULT 0,
synced_files INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users(id)
);
`);

View File

@ -2,6 +2,7 @@ const express = require('express');
const cors = require('cors');
const authRoutes = require('./routes/auth');
const fileRoutes = require('./routes/files');
const shareRoutes = require('./routes/share');
const app = express();
const PORT = process.env.PORT || 3000;
@ -12,6 +13,7 @@ app.use(express.json());
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/files', fileRoutes);
app.use('/api/share', shareRoutes);
// Health check
app.get('/api/health', (req, res) => {

142
backend/src/routes/share.js Normal file
View File

@ -0,0 +1,142 @@
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;