智联笔记项目——251021为分享功能添加有效期
注:在原有增删改查,导出分享功能上强化分享功能,为分享功能添加了有效期。
在日常使用笔记系统时,我们经常需要分享笔记给他人,但永久有效的分享链接存在安全隐患。本文将介绍如何在原有笔记系统基础上,新增分享链接的有效期限制功能,包括后端逻辑和前端交互的实现。
功能背景:
原有笔记系统已支持生成分享链接(分为 "只读" 和 "可编辑" 权限),但链接永久有效。新增需求如下:
- 支持设置分享链接的有效期(1 分钟、3 天、7 天、30 天、永久)
- 过期链接自动失效,无法访问
- 自动清理过期的分享链接,减少冗余数据
后端实现(Python + Flask)
1. 数据模型修改
首先在分享链接模型中添加过期时间字段,修改ShareLink类:
点击查看代码
# 原有代码
class ShareLink(db.Model):
__tablename__ = 'share_link'
id = db.Column(db.Integer, primary_key=True)
note_id = db.Column(db.Integer, db.ForeignKey('note.id', ondelete="CASCADE"), nullable=False)
token = db.Column(db.String(36), unique=True, nullable=False) # uuid4
permission = db.Column(db.String(10), nullable=False) # 'view' / 'edit'
created_at = db.Column(db.DateTime, default=datetime.now)
note = db.relationship('Note', backref='share_links')
# 新增后代码
class ShareLink(db.Model):
__tablename__ = 'share_link'
id = db.Column(db.Integer, primary_key=True)
note_id = db.Column(db.Integer, db.ForeignKey('note.id', ondelete="CASCADE"), nullable=False)
token = db.Column(db.String(36), unique=True, nullable=False) # uuid4
permission = db.Column(db.String(10), nullable=False) # 'view' / 'edit'
created_at = db.Column(db.DateTime, default=datetime.now)
expire_at = db.Column(db.DateTime) # 新增:过期时间,None表示永久有效
note = db.relationship('Note', backref='share_links')
关键修改:
- 添加expire_at字段存储过期时间,None表示永久有效。
2. 生成分享链接 API 优化
修改创建分享链接的接口,支持接收有效期参数并计算过期时间:
点击查看代码
# 原有代码
@app.route('/api/share', methods=['POST'])
def create_share():
data = request.json
note_id = data.get('note_id')
permission = data.get('permission', 'view')
if permission not in ('view', 'edit'):
return jsonify({'error': '无效的权限类型'}), 400
note = Note.query.get_or_404(note_id)
token = generate_token()
link = ShareLink(note_id=note.id, token=token, permission=permission)
db.session.add(link)
db.session.commit()
share_url = url_for('share_page', token=token, _external=True)
return jsonify({'share_url': share_url, 'permission': permission})
# 新增后代码
@app.route('/api/share', methods=['POST'])
def create_share():
data = request.json
note_id = data.get('note_id')
permission = data.get('permission', 'view')
expires_in = data.get('expires_in') # 数值(如1、3)
expires_unit = data.get('expires_unit', 'days') # 单位(minutes/days)
if permission not in ('view', 'edit'):
return jsonify({'error': '无效的权限类型'}), 400
note = Note.query.get_or_404(note_id)
token = generate_token()
expire_at = None
# 计算过期时间(支持分钟和天)
if expires_in is not None:
if expires_unit == 'minutes':
expire_at = datetime.now() + timedelta(minutes=int(expires_in))
else:
expire_at = datetime.now() + timedelta(days=int(expires_in))
link = ShareLink(
note_id=note.id,
token=token,
permission=permission,
expire_at=expire_at # 保存过期时间
)
db.session.add(link)
db.session.commit()
share_url = url_for('share_page', token=token, _external=True)
return jsonify({
'share_url': share_url,
'permission': permission,
'expire_at': expire_at.isoformat() if expire_at else None # 返回过期时间
})
3. 分享链接验证逻辑
在访问分享链接时,新增过期检查:
点击查看代码
# 原有代码
def get_share_by_token(token):
link = ShareLink.query.filter_by(token=token).first()
if not link:
abort(404, description='分享链接不存在')
return link
# 新增后代码
def get_share_by_token(token):
link = ShareLink.query.filter_by(token=token).first()
if not link:
abort(404, description='分享链接不存在')
# 新增:检查是否过期
if link.expire_at and link.expire_at < datetime.now():
abort(403, description='分享链接已过期')
return link
关键修改:
- 添加expire_at < datetime.now()的过期判断,过期则返回 403 错误。
4. 自动清理过期链接
使用定时任务定期清理过期链接,避免数据冗余:
点击查看代码
# 新增代码
def init_scheduler(app):
scheduler.init_app(app)
# 每天凌晨清理过期链接
@scheduler.task('cron', hour=0)
def clean_expired_links():
with app.app_context():
expired_links = ShareLink.query.filter(
ShareLink.expire_at < datetime.now()
).all()
for link in expired_links:
db.session.delete(link)
db.session.commit()
scheduler.start()
前端实现(HTML + JavaScript)
1. 分享弹窗添加有效期选择
在分享弹窗中新增有效期下拉框,让用户选择链接有效期:
点击查看代码
<!-- 原有代码 -->
<div id="share-modal" class="modal">
<div class="modal-content">
<span class="close" id="share-close">×</span>
<h3>生成分享链接</h3>
<select id="share-permission" style="width:100%;margin-top:8px;">
<option value="view">只读(view)</option>
<option value="edit">可编辑(edit)</option>
</select>
<input type="text" id="share-link" readonly placeholder="生成的链接会显示在这里">
<button class="confirm" id="generate-link-btn">生成链接</button>
<button class="cancel" id="copy-link-btn">复制链接</button>
</div>
</div>
<!-- 新增后代码 -->
<div id="share-modal" class="modal">
<div class="modal-content">
<span class="close" id="share-close">×</span>
<h3>生成分享链接</h3>
<select id="share-permission" style="width:100%;margin-top:8px;">
<option value="view">只读(view)</option>
<option value="edit">可编辑(edit)</option>
</select>
<!-- 新增:有效期选择 -->
<select id="share-expiry" style="width:100%;margin-top:8px;">
<option value="1m">1分钟(测试用)</option>
<option value="3">3天</option>
<option value="7" selected>7天</option>
<option value="30">30天</option>
<option value="">永久有效</option>
</select>
<input type="text" id="share-link" readonly placeholder="生成的链接会显示在这里">
<div id="expiry-info" style="color:#666;font-size:12px;margin:8px 0;">
链接有效期信息将在这里显示
</div>
<button class="confirm" id="generate-link-btn">生成链接</button>
<button class="cancel" id="copy-link-btn">复制链接</button>
</div>
</div>
2. 生成链接时传递有效期参数
修改前端生成分享链接的逻辑,将有效期参数传递给后端:
点击查看代码
// 原有代码
async function generateShareLink() {
if (!currentNoteId) return;
const permission = sharePermissionSelect.value;
try {
const res = await fetch('/api/share', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ note_id: currentNoteId, permission })
});
// ...后续逻辑
} catch (e) {
// ...错误处理
}
}
// 新增后代码
async function generateShareLink() {
if (!currentNoteId) return;
const permission = sharePermissionSelect.value;
const expiryValue = shareExpirySelect.value; // 获取选中的有效期值(如"1m"、"3"等)
// 解析有效期(支持分钟和天)
let expiresIn = null;
let unit = 'days'; // 默认单位:天
if (expiryValue.includes('m')) {
// 处理分钟(如"1m")
expiresIn = parseInt(expiryValue.replace('m', ''));
unit = 'minutes';
} else if (expiryValue) {
// 处理天(如"3")
expiresIn = parseInt(expiryValue);
}
try {
const res = await fetch('/api/share', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
note_id: currentNoteId,
permission,
expires_in: expiresIn,
expires_unit: unit // 传递单位(minutes/days)
})
});
if (!res.ok) {
const data = await res.json();
throw new Error(data.error || '生成链接失败');
}
const data = await res.json();
shareLinkInput.value = data.share_url;
// 显示有效期信息
let expiryText = '永久有效';
if (data.expire_at) {
const expireDate = new Date(data.expire_at);
expiryText = `有效期至: ${expireDate.toLocaleString()}`;
}
document.getElementById('expiry-info').textContent = expiryText;
showToast('链接生成成功');
} catch (e) {
console.error('生成分享链接失败:', e);
showToast(e.message || '生成链接失败');
}
}
-
解析share-expiry的值,区分分钟(含 "m")和天(纯数字)
-
向后端传递expires_in(数值)和expires_unit(单位)
-
接收后端返回的expire_at,格式化后显示在expiry-info区域
功能效果
-
用户点击 "分享" 按钮后,可选择权限和有效期
-
生成链接时,前端显示具体的过期时间(如 "有效期至:2024-05-20 12:00:00")
-
过期链接访问时,后端返回 403 错误("分享链接已过期")
-
系统每天自动清理过期链接,保持数据整洁
总结与扩展
通过上述修改,我们为笔记系统的分享功能添加了有效期限制,提升了安全性。后续可扩展的方向:
- 支持自定义有效期(如用户输入具体天数)
- 分享链接列表显示,支持手动提前失效
- 过期前提醒用户链接即将失效
该功能的核心是通过expire_at字段跟踪过期时间,结合前端选择和后端验证,实现完整的有效期管理流程。

浙公网安备 33010602011771号