feat(share): reuse existing share and direct links per file
This commit is contained in:
@@ -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 *
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2497,12 +2497,18 @@ handleDragLeave(e) {
|
||||
if (response.data.success) {
|
||||
const itemType = this.shareFileForm.isDirectory ? '文件夹' : '文件';
|
||||
this.shareResult = this.buildShareResult(response.data, {
|
||||
hasPassword: this.shareFileForm.enablePassword,
|
||||
hasPassword: !!response.data.has_password,
|
||||
targetName: this.shareFileForm.fileName,
|
||||
targetType: shareType,
|
||||
password
|
||||
password: response.data.reused ? '' : password
|
||||
});
|
||||
this.showToast('success', '成功', this.shareFileForm.enablePassword ? `${itemType}加密分享已创建` : `${itemType}分享链接已创建`);
|
||||
this.showToast(
|
||||
'success',
|
||||
'成功',
|
||||
response.data.reused
|
||||
? `${itemType}已复用现有分享链接`
|
||||
: (this.shareFileForm.enablePassword ? `${itemType}加密分享已创建` : `${itemType}分享链接已创建`)
|
||||
);
|
||||
this.loadShares();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2551,7 +2557,7 @@ handleDragLeave(e) {
|
||||
...response.data,
|
||||
target_name: this.directLinkForm.fileName
|
||||
};
|
||||
this.showToast('success', '成功', '直链已创建');
|
||||
this.showToast('success', '成功', response.data.reused ? '已复用现有直链' : '直链已创建');
|
||||
this.loadDirectLinks();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user