feat: v3.0 - 用户系统和收藏功能

核心功能:
- 用户注册/登录系统(用户名+密码)
- 工具收藏功能(一键收藏/取消收藏)
- 收藏分组管理(文件夹)
- 用户中心(个人资料、收藏列表)

数据库变更:
- 新增 users 表(用户信息)
- 新增 folders 表(收藏分组)
- 新增 collections 表(收藏记录)

安全增强:
- Admin 和 User 完全隔离
- 修复14个管理员路由的权限漏洞
- 所有管理功能添加用户类型检查

新增文件:
- templates/auth/register.html - 注册页面
- templates/auth/login.html - 登录页面
- templates/user/profile.html - 用户中心
- templates/user/collections.html - 收藏列表
- create_user_tables.py - 数据库迁移脚本
- USER_SYSTEM_README.md - 用户系统文档
- CHANGELOG_v3.0.md - 版本更新日志

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jowe
2026-02-06 19:19:05 +08:00
parent 34cd05b01c
commit 2067fb1712
11 changed files with 2542 additions and 6 deletions

View File

@@ -727,6 +727,12 @@
<span></span>
</a>
<!-- 收藏按钮 -->
<button type="button" id="collectBtn" class="collect-btn" onclick="toggleCollect()">
<span class="collect-icon" id="collectIcon"></span>
<span class="collect-text" id="collectText">收藏</span>
</button>
<!-- v2.5新增:社媒分享入口 -->
<button type="button" class="share-btn" onclick="openShareModal()">
分享
@@ -1156,6 +1162,45 @@ document.addEventListener('DOMContentLoaded', function() {
</script>
<style>
/* 收藏按钮 */
.collect-btn {
margin-top: 12px;
width: 100%;
padding: 12px;
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
background: var(--bg-white);
color: var(--text-secondary);
font-weight: 600;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
}
.collect-btn:hover {
border-color: var(--primary-blue);
color: var(--primary-blue);
}
.collect-btn.collected {
background: rgba(251, 191, 36, 0.1);
border-color: rgba(251, 191, 36, 0.5);
color: #f59e0b;
}
.collect-btn.collected .collect-icon {
animation: pulse 0.4s ease;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}
.share-btn {
margin-top: 12px;
width: 100%;
@@ -1521,6 +1566,96 @@ function shareToplatform() {
navigator.clipboard.writeText(text).catch(() => {});
}
}
// ========== 收藏功能 ==========
const siteCode = '{{ site.code }}';
let isCollected = false;
// 页面加载时检查收藏状态
document.addEventListener('DOMContentLoaded', function() {
checkCollectionStatus();
});
function checkCollectionStatus() {
fetch('/api/auth/status')
.then(r => r.json())
.then(data => {
if (!data.logged_in || data.user_type !== 'user') {
return; // 未登录或非普通用户,不检查收藏状态
}
// 已登录,检查收藏状态
fetch(`/api/collections/status/${siteCode}`)
.then(r => r.json())
.then(data => {
isCollected = data.is_collected;
updateCollectButton();
})
.catch(err => console.error('检查收藏状态失败:', err));
})
.catch(err => console.error('检查登录状态失败:', err));
}
function updateCollectButton() {
const btn = document.getElementById('collectBtn');
const icon = document.getElementById('collectIcon');
const text = document.getElementById('collectText');
if (isCollected) {
btn.classList.add('collected');
text.textContent = '已收藏';
} else {
btn.classList.remove('collected');
text.textContent = '收藏';
}
}
function toggleCollect() {
// 先检查登录状态
fetch('/api/auth/status')
.then(r => r.json())
.then(data => {
if (!data.logged_in) {
// 未登录,跳转到登录页
if (confirm('请先登录后再收藏工具')) {
window.location.href = `/login?next=${encodeURIComponent(window.location.pathname)}`;
}
return;
}
if (data.user_type !== 'user') {
showMessage('管理员账号无法使用收藏功能', 'error');
return;
}
// 已登录,执行收藏/取消收藏
fetch('/api/collections/toggle', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ site_code: siteCode })
})
.then(r => r.json())
.then(data => {
if (data.success) {
isCollected = data.action === 'added';
updateCollectButton();
showMessage(data.message, 'success');
} else {
showMessage(data.message || '操作失败', 'error');
}
})
.catch(err => {
console.error('收藏操作失败:', err);
showMessage('网络请求失败', 'error');
});
})
.catch(err => {
console.error('检查登录状态失败:', err);
showMessage('网络请求失败', 'error');
});
}
</script>
<style>