feat(share): reuse existing share and direct links per file

This commit is contained in:
2026-02-18 22:13:14 +08:00
parent ada7986669
commit af51d74a9f
3 changed files with 131 additions and 5 deletions

View File

@@ -1354,6 +1354,31 @@ const ShareDB = {
return db.prepare('SELECT * FROM shares WHERE id = ?').get(id);
},
// 按目标路径查找已存在分享(用于复用)
findExistingByTarget(userId, options = {}) {
const {
share_type = 'file',
file_path = '',
storage_type = 'oss'
} = options;
return db.prepare(`
SELECT *
FROM shares
WHERE user_id = ?
AND share_type = ?
AND share_path = ?
AND COALESCE(storage_type, 'oss') = ?
ORDER BY id DESC
LIMIT 1
`).get(
userId,
share_type,
file_path,
storage_type || 'oss'
);
},
// 验证分享密码
verifyPassword(plainPassword, hashedPassword) {
return bcrypt.compareSync(plainPassword, hashedPassword);
@@ -1492,6 +1517,28 @@ const DirectLinkDB = {
return db.prepare('SELECT * FROM direct_links WHERE id = ?').get(id);
},
// 按目标路径查找已存在直链(用于复用)
findExistingByTarget(userId, options = {}) {
const {
file_path = '',
storage_type = 'oss'
} = options;
return db.prepare(`
SELECT *
FROM direct_links
WHERE user_id = ?
AND file_path = ?
AND COALESCE(storage_type, 'oss') = ?
ORDER BY id DESC
LIMIT 1
`).get(
userId,
file_path,
storage_type || 'oss'
);
},
getUserLinks(userId) {
return db.prepare(`
SELECT *

View File

@@ -7263,6 +7263,43 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
});
}
const storageType = req.user.current_storage_type || 'oss';
const existingShare = ShareDB.findExistingByTarget(req.user.id, {
share_type: actualShareType,
file_path: normalizedSharePath,
storage_type: storageType
});
if (existingShare) {
const shareUrl = `${getSecureBaseUrl(req)}/s/${existingShare.share_code}`;
const securityPolicy = getSharePolicySummary(existingShare);
logShare(
req,
'reuse_share',
`用户复用现有分享: ${actualShareType === 'file' ? '文件' : '目录'} ${normalizedSharePath}`,
{
shareId: existingShare.id,
shareCode: existingShare.share_code,
sharePath: normalizedSharePath,
shareType: existingShare.share_type
}
);
return res.json({
success: true,
message: '已复用现有分享链接',
reused: true,
share_id: existingShare.id,
share_code: existingShare.share_code,
share_url: shareUrl,
share_type: existingShare.share_type,
expires_at: existingShare.expires_at,
has_password: !!existingShare.share_password,
security_policy: securityPolicy
});
}
SystemLogDB.log({
level: 'info',
category: 'share',
@@ -7296,7 +7333,7 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
// 更新分享的存储类型
db.prepare('UPDATE shares SET storage_type = ? WHERE id = ?')
.run(req.user.current_storage_type || 'oss', result.id);
.run(storageType, result.id);
const shareUrl = `${getSecureBaseUrl(req)}/s/${result.share_code}`;
@@ -7331,6 +7368,7 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
res.json({
success: true,
message: '分享链接创建成功',
reused: false,
share_code: result.share_code,
share_url: shareUrl,
share_type: result.share_type,
@@ -7489,6 +7527,40 @@ app.post('/api/direct-link/create',
: (normalizedPath.split('/').pop() || 'download.bin');
const storageType = req.user.current_storage_type || 'oss';
const existingLink = DirectLinkDB.findExistingByTarget(req.user.id, {
file_path: normalizedPath,
storage_type: storageType
});
if (existingLink) {
const directUrl = `${getSecureBaseUrl(req)}/d/${existingLink.link_code}`;
logShare(
req,
'reuse_direct_link',
`用户复用直链: ${normalizedPath}`,
{
linkId: existingLink.id,
linkCode: existingLink.link_code,
filePath: normalizedPath,
storageType
}
);
return res.json({
success: true,
message: '已复用现有直链',
reused: true,
link_id: existingLink.id,
link_code: existingLink.link_code,
file_path: normalizedPath,
file_name: existingLink.file_name || resolvedFileName,
storage_type: storageType,
expires_at: existingLink.expires_at || null,
direct_url: directUrl
});
}
const directLink = DirectLinkDB.create(req.user.id, {
file_path: normalizedPath,
file_name: resolvedFileName,
@@ -7514,6 +7586,7 @@ app.post('/api/direct-link/create',
res.json({
success: true,
message: '直链创建成功',
reused: false,
link_id: directLink.id,
link_code: directLink.link_code,
file_path: normalizedPath,