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);
|
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) {
|
verifyPassword(plainPassword, hashedPassword) {
|
||||||
return bcrypt.compareSync(plainPassword, hashedPassword);
|
return bcrypt.compareSync(plainPassword, hashedPassword);
|
||||||
@@ -1492,6 +1517,28 @@ const DirectLinkDB = {
|
|||||||
return db.prepare('SELECT * FROM direct_links WHERE id = ?').get(id);
|
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) {
|
getUserLinks(userId) {
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
SELECT *
|
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({
|
SystemLogDB.log({
|
||||||
level: 'info',
|
level: 'info',
|
||||||
category: 'share',
|
category: 'share',
|
||||||
@@ -7296,7 +7333,7 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
|
|||||||
|
|
||||||
// 更新分享的存储类型
|
// 更新分享的存储类型
|
||||||
db.prepare('UPDATE shares SET storage_type = ? WHERE id = ?')
|
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}`;
|
const shareUrl = `${getSecureBaseUrl(req)}/s/${result.share_code}`;
|
||||||
|
|
||||||
@@ -7331,6 +7368,7 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: '分享链接创建成功',
|
message: '分享链接创建成功',
|
||||||
|
reused: false,
|
||||||
share_code: result.share_code,
|
share_code: result.share_code,
|
||||||
share_url: shareUrl,
|
share_url: shareUrl,
|
||||||
share_type: result.share_type,
|
share_type: result.share_type,
|
||||||
@@ -7489,6 +7527,40 @@ app.post('/api/direct-link/create',
|
|||||||
: (normalizedPath.split('/').pop() || 'download.bin');
|
: (normalizedPath.split('/').pop() || 'download.bin');
|
||||||
|
|
||||||
const storageType = req.user.current_storage_type || 'oss';
|
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, {
|
const directLink = DirectLinkDB.create(req.user.id, {
|
||||||
file_path: normalizedPath,
|
file_path: normalizedPath,
|
||||||
file_name: resolvedFileName,
|
file_name: resolvedFileName,
|
||||||
@@ -7514,6 +7586,7 @@ app.post('/api/direct-link/create',
|
|||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: '直链创建成功',
|
message: '直链创建成功',
|
||||||
|
reused: false,
|
||||||
link_id: directLink.id,
|
link_id: directLink.id,
|
||||||
link_code: directLink.link_code,
|
link_code: directLink.link_code,
|
||||||
file_path: normalizedPath,
|
file_path: normalizedPath,
|
||||||
|
|||||||
@@ -2497,12 +2497,18 @@ handleDragLeave(e) {
|
|||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
const itemType = this.shareFileForm.isDirectory ? '文件夹' : '文件';
|
const itemType = this.shareFileForm.isDirectory ? '文件夹' : '文件';
|
||||||
this.shareResult = this.buildShareResult(response.data, {
|
this.shareResult = this.buildShareResult(response.data, {
|
||||||
hasPassword: this.shareFileForm.enablePassword,
|
hasPassword: !!response.data.has_password,
|
||||||
targetName: this.shareFileForm.fileName,
|
targetName: this.shareFileForm.fileName,
|
||||||
targetType: shareType,
|
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();
|
this.loadShares();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2551,7 +2557,7 @@ handleDragLeave(e) {
|
|||||||
...response.data,
|
...response.data,
|
||||||
target_name: this.directLinkForm.fileName
|
target_name: this.directLinkForm.fileName
|
||||||
};
|
};
|
||||||
this.showToast('success', '成功', '直链已创建');
|
this.showToast('success', '成功', response.data.reused ? '已复用现有直链' : '直链已创建');
|
||||||
this.loadDirectLinks();
|
this.loadDirectLinks();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user