首页
文章导航
导航
壁纸
留言板
更多
直播
友链
统计
关于
Search
1
bpb面板搭建【有时效性,可能1001用不了】
13 阅读
2
cloudflare上免费部署随机地址生成器
12 阅读
3
Claw Cloud 免费容器平台部署哪吒面板教程
12 阅读
4
【白嫖攻略】在cloudflare搭建域名邮箱并转发
12 阅读
5
经典老电影10部
9 阅读
默认
日常
学习
技术
登录
Search
标签搜索
cloudflare
白嫖
CF
docker
安装
脚本
图片
壁纸
Linux
Caddy
代码
哪吒
节点
域名
搭建
github
网页
HTML
LXC
手机壁纸
ws01
累计撰写
124
篇文章
累计收到
45
条评论
首页
栏目
默认
日常
学习
技术
页面
文章导航
导航
壁纸
留言板
直播
友链
统计
关于
搜索到
1
篇与
的结果
2025-10-19
CF _worker搭建 WS01 Note-20251019
CF _worker搭建 WS01 Note,可设置私人日记或公开分享变量设置:1、帐号:USERNAME,默认:9527a2、密码:PASSWORD,默认:9527abc3、KV空间邦定:WS01_NOTE_KV// WS01 Note - Cloudflare Workers + KV 日记本应用 // 作者: WS01 // 功能: 基于Cloudflare Workers和KV数据库的简单日记本 export default { async fetch(request, env, ctx) { const url = new URL(request.url); const path = url.pathname; // 处理CORS const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }; if (request.method === 'OPTIONS') { return new Response(null, { status: 200, headers: corsHeaders }); } try { // 路由处理 if (path === '/' || path === '/login') { return new Response(getLoginPage(), { headers: { 'Content-Type': 'text/html; charset=utf-8', ...corsHeaders } }); } if (path === '/diary') { return new Response(getDiaryPage(), { headers: { 'Content-Type': 'text/html; charset=utf-8', ...corsHeaders } }); } // 查看具体日记页面 if (path.startsWith('/diary/') && path.split('/').length === 3) { const diaryId = path.split('/')[2]; return new Response(getDiaryDetailPage(diaryId), { headers: { 'Content-Type': 'text/html; charset=utf-8', ...corsHeaders } }); } // 分享日记目录页面路由 if (path === '/share') { return new Response(getShareIndexPage(), { headers: { 'Content-Type': 'text/html; charset=utf-8', ...corsHeaders } }); } // 分享页面路由 if (path.startsWith('/share/') && path.split('/').length === 3) { const shareId = path.split('/')[2]; return new Response(getSharePage(shareId), { headers: { 'Content-Type': 'text/html; charset=utf-8', ...corsHeaders } }); } if (path === '/api/auth') { return handleAuth(request, env, corsHeaders); } if (path === '/api/diary') { return handleDiaryAPI(request, env, corsHeaders); } // 添加分享相关API路由 if (path === '/api/diary/share' && (request.method === 'POST' || request.method === 'PUT')) { return handleShareDiary(request, env, corsHeaders); } // 获取分享日记API路由 if (path.startsWith('/api/share/') && path.split('/').length === 4) { const shareId = path.split('/')[3]; return handleGetSharedDiary(shareId, env, corsHeaders); } // 获取所有分享日记API路由 if (path === '/api/shares' && request.method === 'GET') { return handleGetAllShares(request, env, corsHeaders); } // 获取用户分享日记列表API路由 if (path === '/api/diary/shares' && request.method === 'GET') { return handleGetUserShares(request, env, corsHeaders); } // 删除分享日记API路由 if (path === '/api/diary/share' && request.method === 'DELETE') { return handleDeleteShare(request, env, corsHeaders); } // 添加备份相关API路由 if (path === '/api/diary/backup' && request.method === 'POST') { return handleCreateBackup(request, env, corsHeaders); } if (path === '/api/diary/backups' && request.method === 'GET') { return handleGetBackups(request, env, corsHeaders); } if (path === '/api/diary/restore' && request.method === 'POST') { return handleRestoreBackup(request, env, corsHeaders); } if (path === '/api/diary/backup' && request.method === 'DELETE') { return handleDeleteBackup(request, env, corsHeaders); } // 404处理 return new Response('页面未找到', { status: 404, headers: { 'Content-Type': 'text/plain; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('Worker错误:', error); return new Response('服务器内部错误', { status: 500, headers: { 'Content-Type': 'text/plain; charset=utf-8', ...corsHeaders } }); } } }; // 登录页面 function getLoginPage() { return ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01 Note - 登录</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; } .login-container { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 10px 25px rgba(0,0,0,0.08); width: 100%; max-width: 360px; } .logo { text-align: center; margin-bottom: 1.5rem; } .logo h1 { color: #333; font-size: 1.6rem; font-weight: 400; } .logo p { color: #666; margin-top: 0.3rem; font-size: 0.9rem; } .form-group { margin-bottom: 1.2rem; } .form-group label { display: block; margin-bottom: 0.4rem; color: #333; font-weight: 500; font-size: 0.9rem; } .form-group input { width: 100%; padding: 0.6rem; border: 1px solid #e1e5e9; border-radius: 6px; font-size: 0.9rem; transition: border-color 0.3s; } .form-group input:focus { outline: none; border-color: #667eea; } .login-btn { width: 100%; padding: 0.6rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 6px; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: transform 0.2s; } .login-btn:hover { transform: translateY(-2px); } .error-message { color: #e74c3c; text-align: center; margin-top: 1rem; display: none; } </style> </head> <body> <div class="login-container"> <div class="logo"> <h1>WS01 Note</h1> <p>您的私人日记本</p> </div> <form id="loginForm"> <div class="form-group"> <label for="username">用户名</label> <input type="text" id="username" name="username" required> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" id="password" name="password" required> </div> <button type="submit" class="login-btn">登录</button> </form> <div id="errorMessage" class="error-message"></div> </div> <script> document.getElementById('loginForm').addEventListener('submit', async (e) => { e.preventDefault(); const username = document.getElementById('username').value; const password = document.getElementById('password').value; const errorDiv = document.getElementById('errorMessage'); try { const response = await fetch('/api/auth', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username, password }) }); const result = await response.json(); if (result.success) { localStorage.setItem('ws01_token', result.token); window.location.href = '/diary'; } else { errorDiv.textContent = result.message || '登录失败'; errorDiv.style.display = 'block'; } } catch (error) { errorDiv.textContent = '网络错误,请重试'; errorDiv.style.display = 'block'; } }); </script> </body> </html>`; } // 日记页面 function getDiaryPage() { return ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01 Note - 我的日记</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa; min-height: 100vh; } .sites { width:1000px; background:#F2F2F2; border:1px solid rgba(0,0,0,.06); margin:10px auto; padding:0px; color: white; border-radius: 6px; } .sites01 { width:1280px; background:; border:2px solid auto; margin:15px auto; padding:0px; } .sites dl { height:36px; line-height:36px; display:block; margin:0; } .sites dl.alt { background:#c5dff6; border-top:1px solid #ffffff; border-bottom:1px solid #ffffff; } .sites dl.alt2 { background:#dcecfa; border-top:1px solid #ffffff; border-bottom:1px solid #ffffff; } .sites dt,.sites dd { text-align:center; display:block; float:left; } .sites dt { width:60px; } .sites dd { width:90px; margin:0; } .header { background: #D4D4D4; padding: 0.8rem 1.5rem; box-shadow: 0 1px 6px rgba(0,0,0,0.08); display: flex; justify-content: center; align-items: center; position: relative; } .logo { font-size: 1.5rem; font-weight: 400; color: #333; } .logout-btn { background: #e74c3c; color: white; border: none; width: 40px; height: 40px; border-radius: 50%; cursor: pointer; font-size: 1.2rem; position: absolute; right: 2.6rem; display: flex; align-items: center; justify-content: center; transition: transform 0.2s; } .logout-btn:hover { background: #c0392b; transform: scale(1.1); } .container { max-width: 960px; margin: 1.5rem auto; padding: 0 1.5rem; display: flex; gap: 1.5rem; } .main-content { flex: 1; min-width: 0; } .sidebar { width: 300px; flex-shrink: 0; } /* 搜索框样式 */ .search-section { margin-bottom: 1rem; } .search-container { position: relative; background: white; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); overflow: hidden; } .search-input { width: 100%; padding: 0.8rem 2.5rem 0.8rem 0.8rem; border: none; border-radius: 8px; font-size: 0.9rem; background: white; transition: all 0.3s ease; } .search-input:focus { outline: none; box-shadow: 0 0 0 2px #667eea; } .search-input::placeholder { color: #999; font-size: 0.85rem; } .clear-search-btn { position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; color: #999; font-size: 1.2rem; cursor: pointer; padding: 0.2rem; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; } .clear-search-btn:hover { background: #f0f0f0; color: #666; } .clear-search-btn.hidden { display: none; } .diary-form { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); } .diary-form-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.2rem; } .diary-form-title { color: #333; margin: 0; font-size: 1.3rem; } .current-time { color: #666; font-size: 0.8rem; } /* 添加保存按钮图标样式 */ .save-btn-icon { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 1rem; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: transform 0.2s; } .save-btn-icon:hover { transform: scale(1.1); } /* 添加分享按钮样式 */ .share-btn-icon { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border: none; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 1rem; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: transform 0.2s; } .share-btn-icon:hover { transform: scale(1.1); } /* 添加分享按钮样式 */ .share01-btn-icon { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border: none; width: 40px; height: 40px; border-radius: 50%; cursor: pointer; font-size: 1.2rem; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: transform 0.2s; text-decoration: none; } .share01-btn-icon:hover { transform: scale(1.1); } .header-with-button { display: flex; justify-content: space-between; align-items: center; } .header-controls { display: flex; align-items: center; gap: 0.5rem; } .font-size-select { padding: 0.3rem 0.5rem; border: 1px solid #e1e5e9; border-radius: 4px; font-size: 0.8rem; background: white; cursor: pointer; transition: border-color 0.3s; } .font-size-select:focus { outline: none; border-color: #667eea; } .font-size-select:hover { border-color: #667eea; } .form-group { margin-bottom: 1.2rem; } .form-group label { display: block; margin-bottom: 0.4rem; color: #333; font-weight: 500; font-size: 0.9rem; } .form-group input, .form-group textarea { width: 100%; padding: 0.6rem; border: 1px solid #e1e5e9; border-radius: 6px; font-size: 0.9rem; font-family: inherit; transition: border-color 0.3s; } .form-group input:focus, .form-group textarea:focus { outline: none; border-color: #667eea; } .form-group textarea { width: 100%; padding: 0.6rem; border: 1px solid #e1e5e9; border-radius: 6px; font-size: 0.9rem; font-family: inherit; transition: border-color 0.3s; min-height: 720px; //写日记模块的高度 } /* 添加字符计数器样式 */ .char-count { font-size: 0.8rem; color: #666; text-align: right; margin-top: 0.25rem; } .char-count.warning { color: #ffc107; } .char-count.error { color: #dc3545; } .save-btn { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 0.75rem 2rem; border-radius: 8px; font-size: 1rem; font-weight: 500; cursor: pointer; transition: transform 0.2s; } .save-btn:hover { transform: translateY(-2px); } .diary-list { background: white; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); overflow: hidden; height: calc(100vh - 410px); display: flex; flex-direction: column; } .diary-list-header { padding: 1.2rem; margin: 0; color: #333; border-bottom: 1px solid #f0f0f0; background: #f8f9fa; font-size: 1rem; font-weight: 600; } .diary-list-content { flex: 1; overflow-y: auto; padding: 0; } .diary-date-group { margin-bottom: 0.8rem; } .date-header { background: #e9ecef; padding: 0.4rem 0.8rem; font-weight: 600; color: #495057; border-bottom: 1px solid #dee2e6; font-size: 0.8rem; position: sticky; top: 0; z-index: 1; } .diary-items { background: white; } .diary-item { padding: 0.6rem 0.8rem; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background-color 0.2s; display: flex; flex-direction: column; gap: 0.2rem; } .diary-item:hover { background-color: #f8f9fa; } .diary-item:last-child { border-bottom: none; } .diary-title { color: #333; font-size: 0.85rem; font-weight: 500; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .diary-time { color: #666; font-size: 0.7rem; align-self: flex-end; } /* 修改: 按钮容器样式 */ .diary-actions { display: flex; gap: 0.4rem; margin-top: 0.3rem; justify-content: flex-end; } .edit-btn, .delete-btn { padding: 0.2rem 0.4rem; font-size: 0.7rem; border: none; border-radius: 3px; cursor: pointer; } .edit-btn { background-color: #007bff; color: white; } .delete-btn { background-color: #dc3545; color: white; } .edit-btn:hover { background-color: #0056b3; } .delete-btn:hover { background-color: #c82333; } .empty-state { text-align: center; padding: 3rem; color: #666; } .message { padding: 1rem; margin-bottom: 1rem; border-radius: 8px; display: none; } .message.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .message.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } /* 添加分享日记模块样式 */ .share-section { margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid #eee; } .share-controls { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; } .share-list { flex: 1; overflow-y: auto; padding: 0; height: calc(100vh - 520px); } .share-date-group { margin-bottom: 0.8rem; } .share-items { background: white; } .share-item { padding: 0.6rem 0.8rem; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background-color 0.2s; display: flex; flex-direction: column; gap: 0.2rem; } .share-item:hover { background-color: #f8f9fa; } .share-item:last-child { border-bottom: none; } .share-title { color: #333; font-size: 0.85rem; font-weight: 500; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .share-time { color: #666; font-size: 0.7rem; align-self: flex-end; } .share-actions { display: flex; gap: 0.4rem; margin-top: 0.3rem; justify-content: flex-end; } .edit-share-btn, .delete-share-btn { padding: 0.2rem 0.4rem; font-size: 0.7rem; border: none; border-radius: 3px; cursor: pointer; } .edit-share-btn { background-color: #007bff; color: white; } .delete-share-btn { background-color: #dc3545; color: white; } .edit-share-btn:hover { background-color: #0056b3; } .delete-share-btn:hover { background-color: #c82333; } /* 添加备份恢复按钮样式 */ .backup-section { margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid #eee; } .backup-controls { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; } .backup-btn { padding: 0.4rem 0.8rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.8rem; } .backup-btn.primary { background: #28a745; color: white; } .backup-btn.secondary { background: #17a2b8; color: white; } .backup-list { max-height: 150px; overflow-y: auto; border: 1px solid #ddd; border-radius: 4px; padding: 0.4rem; } .backup-item { background: #ffffff; color: #969696; padding: 0.4rem; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; } .backup-item:last-child { border-bottom: none; } .restore-btn { background: #ffc107; color: black; border: none; padding: 0.2rem 0.4rem; border-radius: 3px; cursor: pointer; font-size: 0.7rem; } .delete-backup-btn { background: #dc3545; color: white; border: none; padding: 0.2rem 0.4rem; border-radius: 3px; cursor: pointer; font-size: 0.7rem; } /* 响应式设计 */ @media (max-width: 768px) { .sites { width: 95%; margin: 5px auto; } .container { flex-direction: column; gap: 0.8rem; margin: 1rem auto; padding: 0 1rem; } .sidebar { width: 100%; order: -1; } .diary-list { height: 300px; } .diary-form { padding: 1rem; } .form-group textarea { min-height: 200px; } /* 隐藏手机端的时间显示 */ .current-time { display: none; } } </style> </head> <body> <div class="sites"> <div class="header"> <div class="logo">WS01 Note</div> <a href="/share" class="share01-btn-icon" target="_blank" title="分享目录">📂</a> <button class="logout-btn" onclick="logout()" title="退出登录">🚪</button> </div> <div class="container"> <div id="message" class="message"></div> <div class="main-content"> <div class="diary-form"> <div class="diary-form-header"> <div class="header-with-button"> <h2 class="diary-form-title">写日记</h2> <div class="header-controls"> <select id="fontSizeSelect" class="font-size-select" title="选择字体大小"> <option value="12">12px</option> <option value="14">14px</option> <option value="16" selected>16px</option> <option value="18">18px</option> <option value="20">20px</option> <option value="22">22px</option> </select> <button type="submit" class="save-btn-icon" title="私人保存">💾</button> <button type="button" class="share-btn-icon" title="分享日记">🔗</button> </div> </div> <div class="current-time" id="currentTime"></div> </div> <form id="diaryForm"> <div class="form-group"> <!-- <label for="diaryTitle">标题</label> --> <input type="text" id="diaryTitle" name="title" placeholder="标题..." required> </div> <div class="form-group"> <!-- <label for="diaryContent">内容</label> --> <textarea id="diaryContent" name="content" placeholder="内容..." required></textarea> <div class="char-count" id="charCount">0 / 100000</div> </div> <!-- 删除原来的保存按钮 --> </form> </div> </div> <div class="sidebar"> <!-- 搜索框 --> <div class="search-section"> <div class="search-container"> <input type="text" id="searchInput" class="search-input" placeholder="搜索日记标题或内容..."> <button id="clearSearch" class="clear-search-btn" title="清除搜索">×</button> </div> </div> <div class="diary-list"> <div class="diary-list-header">我的日记</div> <div class="diary-list-content" id="diaryList"> <div class="empty-state">还没有日记,开始记录吧!</div> </div> </div> <!-- 分享日记模块 --> <div class="share-section"> <div class="diary-list-header">分享日记</div> <div class="share-controls"> <button class="backup-btn secondary" onclick="loadSharedDiaries()">刷新分享列表</button> </div> <div class="share-list" id="shareList"> <div class="empty-state">暂无分享日记</div> </div> </div> <!-- 移动备份和恢复功能到这里 --> <div class="backup-section"> <div class="diary-list-header">数据备份与恢复</div> <div class="backup-controls"> <button class="backup-btn primary" onclick="createBackup()">创建备份</button> <button class="backup-btn secondary" onclick="loadBackups()">刷新备份列表</button> </div> <div class="backup-list" id="backupList"> <div class="empty-state">暂无备份</div> </div> </div> </div> </div> <script> // 更新当前时间 function updateTime() { const now = new Date(); const timeString = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); document.getElementById('currentTime').textContent = timeString; } // HTML转义函数 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // HTML反转义函数 function unescapeHtml(html) { const div = document.createElement('div'); div.innerHTML = html; return div.textContent || div.innerText || ''; } // 添加: 更新字符计数函数 function updateCharCount() { const content = document.getElementById('diaryContent'); const charCount = document.getElementById('charCount'); const currentLength = content.value.length; charCount.textContent = \`\${currentLength} / 100000\`; // 根据字符数量改变颜色 if (currentLength > 1800) { charCount.className = 'char-count error'; } else if (currentLength > 1500) { charCount.className = 'char-count warning'; } else { charCount.className = 'char-count'; } // 超过限制时截断内容 if (currentLength > 100000) { content.value = content.value.substring(0, 100000); charCount.textContent = '100000 / 100000'; } } // 添加: 字号选择功能 function changeFontSize() { const fontSizeSelect = document.getElementById('fontSizeSelect'); const contentTextarea = document.getElementById('diaryContent'); const selectedSize = fontSizeSelect.value; // 应用字体大小到内容区域 contentTextarea.style.fontSize = selectedSize + 'px'; // 保存用户选择到本地存储 localStorage.setItem('ws01_font_size', selectedSize); } // 添加: 加载保存的字体大小 function loadFontSize() { const savedSize = localStorage.getItem('ws01_font_size'); const fontSizeSelect = document.getElementById('fontSizeSelect'); const contentTextarea = document.getElementById('diaryContent'); if (savedSize) { fontSizeSelect.value = savedSize; contentTextarea.style.fontSize = savedSize + 'px'; } else { // 默认字体大小 contentTextarea.style.fontSize = '14px'; } } // 每秒更新时间 setInterval(updateTime, 1000); updateTime(); // 检查登录状态 function checkAuth() { const token = localStorage.getItem('ws01_token'); if (!token) { window.location.href = '/login'; return false; } return true; } // 显示消息 function showMessage(message, type = 'success') { const messageDiv = document.getElementById('message'); messageDiv.textContent = message; messageDiv.className = \`message \${type}\`; messageDiv.style.display = 'block'; setTimeout(() => { messageDiv.style.display = 'none'; }, 3000); } // 加载日记列表 async function loadDiaries() { try { const response = await fetch('/api/diary', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); const result = await response.json(); if (result.success) { allDiaries = result.diaries; // 存储所有日记数据 // 检查是否有搜索条件,如果有则重新执行搜索 const searchTerm = document.getElementById('searchInput').value.trim(); if (searchTerm) { performSearch(); // 重新执行搜索 } else { filteredDiaries = [...allDiaries]; // 初始化过滤后的数据 displayDiaries(filteredDiaries); } } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('加载日记失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 显示日记列表 function displayDiaries(diaries) { const diaryList = document.getElementById('diaryList'); const searchTerm = document.getElementById('searchInput').value.trim(); if (diaries.length === 0) { if (searchTerm) { diaryList.innerHTML = '<div class="empty-state">没有找到匹配的日记</div>'; } else { diaryList.innerHTML = '<div class="empty-state">还没有日记,开始记录吧!</div>'; } return; } // 按日期分组日记 const groupedDiaries = {}; diaries.forEach(diary => { const date = new Date(diary.date).toLocaleDateString('zh-CN'); if (!groupedDiaries[date]) { groupedDiaries[date] = []; } groupedDiaries[date].push(diary); }); // 生成HTML let html = ''; const sortedDates = Object.keys(groupedDiaries).sort((a, b) => new Date(b) - new Date(a)); sortedDates.forEach(date => { html += \`<div class="diary-date-group"> <div class="date-header">\${date}</div> <div class="diary-items">\`; groupedDiaries[date].forEach(diary => { // 限制标题显示长度 const maxTitleLength = 15; const displayTitle = diary.title.length > maxTitleLength ? diary.title.substring(0, maxTitleLength) + '...' : diary.title; html += \`<div class="diary-item" onclick="viewDiary('\${diary.id}')"> <div style="display: flex; justify-content: space-between; align-items: center;"> <div class="diary-title">\${displayTitle}</div> <div class="diary-actions"> <button class="edit-btn" onclick="editDiary(event, '\${diary.id}')" title="编辑">✎</button> <button class="delete-btn" onclick="deleteDiary(event, '\${diary.id}')" title="删除">🗑</button> </div> </div> <!-- 删除时间显示 --> </div>\`; }); html += \`</div></div>\`; }); diaryList.innerHTML = html; } // 添加: 编辑日记功能 function editDiary(event, diaryId) { event.stopPropagation(); // 防止触发查看日记 // 查找要编辑的日记 fetch('/api/diary', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }) .then(response => response.json()) .then(result => { if (result.success) { const diary = result.diaries.find(d => d.id === diaryId); if (diary) { // 填充表单(反转义HTML字符) document.getElementById('diaryTitle').value = unescapeHtml(diary.title); document.getElementById('diaryContent').value = unescapeHtml(diary.content); // 保存当前编辑的日记ID到表单属性中 document.getElementById('diaryForm').setAttribute('data-edit-id', diaryId); // 更改按钮文字为"更新日记" document.querySelector('.save-btn').textContent = '更新日记'; // 滚动到表单顶部 document.querySelector('.diary-form').scrollIntoView({ behavior: 'smooth' }); } else { showMessage('找不到要编辑的日记', 'error'); } } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('加载失败: ' + result.message, 'error'); } } }) .catch(error => { showMessage('网络错误,请重试', 'error'); }); } // 添加: 删除日记功能 function deleteDiary(event, diaryId) { event.stopPropagation(); // 防止触发查看日记 if (!confirm('确定要删除这篇日记吗?')) { return; } fetch('/api/diary', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: JSON.stringify({ id: diaryId }) }) .then(response => response.json()) .then(result => { if (result.success) { showMessage('日记删除成功!'); loadDiaries(); // 重新加载日记列表,这会更新allDiaries和filteredDiaries // 如果正在编辑被删除的日记,重置表单 const form = document.getElementById('diaryForm'); if (form.getAttribute('data-edit-id') === diaryId) { form.reset(); form.removeAttribute('data-edit-id'); document.querySelector('.save-btn').textContent = '保存日记'; } } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('删除失败: ' + result.message, 'error'); } } }) .catch(error => { showMessage('网络错误,请重试', 'error'); }); } // 保存日记 document.getElementById('diaryForm').addEventListener('submit', async (e) => { e.preventDefault(); if (!checkAuth()) return; const title = document.getElementById('diaryTitle').value; const content = document.getElementById('diaryContent').value; const editId = document.getElementById('diaryForm').getAttribute('data-edit-id'); const editShareId = document.getElementById('diaryForm').getAttribute('data-edit-share-id'); // 对HTML特殊字符进行转义 const escapedTitle = escapeHtml(title); const escapedContent = escapeHtml(content); try { const method = editId ? 'PUT' : 'POST'; const body = editId ? JSON.stringify({ id: editId, title: escapedTitle, content: escapedContent }) : JSON.stringify({ title: escapedTitle, content: escapedContent, editShareId: editShareId || null }); const response = await fetch('/api/diary', { method: method, headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: body }); const result = await response.json(); if (result.success) { if (editId) { showMessage('日记更新成功!'); // 重置表单状态 document.getElementById('diaryForm').removeAttribute('data-edit-id'); document.querySelector('.save-btn').textContent = '保存日记'; } else { showMessage('日记保存成功!'); document.getElementById('diaryForm').reset(); // 如果是从分享日记编辑而来,重置表单状态 if (editShareId) { document.getElementById('diaryForm').removeAttribute('data-edit-share-id'); } } // 刷新两个列表 loadDiaries(); loadSharedDiaries(); } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('保存失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } }); // 添加: 为保存按钮图标添加点击事件监听器 document.querySelector('.save-btn-icon').addEventListener('click', function() { document.getElementById('diaryForm').dispatchEvent(new Event('submit')); }); // 添加: 为分享按钮图标添加点击事件监听器 document.querySelector('.share-btn-icon').addEventListener('click', function() { shareDiary(); }); // 分享日记功能 async function shareDiary() { if (!checkAuth()) return; const title = document.getElementById('diaryTitle').value; const content = document.getElementById('diaryContent').value; const editShareId = document.getElementById('diaryForm').getAttribute('data-edit-share-id'); const editDiaryId = document.getElementById('diaryForm').getAttribute('data-edit-id'); if (!title.trim() || !content.trim()) { showMessage('请先填写标题和内容', 'error'); return; } // 对HTML特殊字符进行转义 const escapedTitle = escapeHtml(title); const escapedContent = escapeHtml(content); try { const method = editShareId ? 'PUT' : 'POST'; const body = editShareId ? JSON.stringify({ shareId: editShareId, title: escapedTitle, content: escapedContent }) : JSON.stringify({ title: escapedTitle, content: escapedContent, editDiaryId: editDiaryId || null }); const response = await fetch('/api/diary/share', { method: method, headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: body }); const result = await response.json(); if (result.success) { if (editShareId) { // 更新分享日记 const shareUrl = \`\${window.location.origin}/share/\${editShareId}\`; showMessage(\`分享日记更新成功!链接:\${shareUrl}\`, 'success'); // 重置表单状态 document.getElementById('diaryForm').removeAttribute('data-edit-share-id'); } else { // 新建分享日记 const shareUrl = \`\${window.location.origin}/share/\${result.shareId}\`; showMessage(\`分享成功!链接:\${shareUrl}\`, 'success'); // 如果是从我的日记编辑而来,重置表单状态 if (editDiaryId) { document.getElementById('diaryForm').removeAttribute('data-edit-id'); } } // 可选:复制链接到剪贴板 if (navigator.clipboard) { const shareUrl = editShareId ? \`\${window.location.origin}/share/\${editShareId}\` : \`\${window.location.origin}/share/\${result.shareId}\`; navigator.clipboard.writeText(shareUrl).then(() => { console.log('分享链接已复制到剪贴板'); }); } // 刷新两个列表 loadDiaries(); loadSharedDiaries(); } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('分享失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 查看具体日记页面 function viewDiary(diaryId) { window.location.href = \`/diary/\${diaryId}\`; } // 退出登录 function logout() { localStorage.removeItem('ws01_token'); window.location.href = '/login'; } // 添加备份相关函数 async function createBackup() { try { const response = await fetch('/api/diary/backup', { method: 'POST', headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); const result = await response.json(); if (result.success) { showMessage('备份创建成功!'); loadBackups(); } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('备份创建失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } async function loadBackups() { try { const response = await fetch('/api/diary/backups', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); const result = await response.json(); if (result.success) { displayBackups(result.backups); } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('加载备份列表失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } function displayBackups(backups) { const backupList = document.getElementById('backupList'); if (backups.length === 0) { backupList.innerHTML = '<div class="empty-state">暂无备份</div>'; return; } let html = ''; backups.forEach(backup => { const date = new Date(backup.timestamp).toLocaleString('zh-CN'); html += \` <div class="backup-item"> <div> <div>备份 #\${backup.id}</div> <div style="font-size: 0.8rem; color: #666;">\${date}</div> <div style="font-size: 0.8rem; color: #666;">包含 \${backup.count} 条日记\${backup.shareCount > 0 ? ',' + backup.shareCount + ' 条分享日记' : ''}</div> </div> <div> <button class="restore-btn" onclick="restoreBackup('\${backup.id}')">恢复</button> <button class="delete-backup-btn" onclick="deleteBackup('\${backup.id}')">删除</button> </div> </div>\`; }); backupList.innerHTML = html; } async function restoreBackup(backupId) { if (!confirm('确定要恢复此备份吗?这将覆盖当前所有日记数据!')) { return; } try { const response = await fetch('/api/diary/restore', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: JSON.stringify({ backupId }) }); const result = await response.json(); if (result.success) { showMessage('数据恢复成功!'); loadDiaries(); // 重新加载日记列表 } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('数据恢复失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } async function deleteBackup(backupId) { if (!confirm('确定要删除此备份吗?')) { return; } try { const response = await fetch('/api/diary/backup', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: JSON.stringify({ backupId }) }); const result = await response.json(); if (result.success) { showMessage('备份删除成功!'); loadBackups(); // 重新加载备份列表 } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('备份删除失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 搜索相关变量 let allDiaries = []; // 存储所有日记数据 let filteredDiaries = []; // 存储过滤后的日记数据 // 搜索功能 async function performSearch() { const searchTerm = document.getElementById('searchInput').value.toLowerCase().trim(); const clearBtn = document.getElementById('clearSearch'); if (searchTerm === '') { // 如果搜索框为空,显示所有日记 filteredDiaries = [...allDiaries]; clearBtn.classList.add('hidden'); displayDiaries(filteredDiaries); } else { // 搜索我的日记 const myDiaryResults = allDiaries.filter(diary => diary.title.toLowerCase().includes(searchTerm) || diary.content.toLowerCase().includes(searchTerm) ); // 搜索分享日记 try { const response = await fetch('/api/diary/shares', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); if (response.ok) { const result = await response.json(); const shareResults = result.shares.filter(share => share.title.toLowerCase().includes(searchTerm) || share.content.toLowerCase().includes(searchTerm) ); // 合并搜索结果 const combinedResults = [ ...myDiaryResults.map(diary => ({ ...diary, type: 'private' })), ...shareResults.map(share => ({ ...share, type: 'shared' })) ]; // 按日期排序 combinedResults.sort((a, b) => new Date(b.date) - new Date(a.date)); filteredDiaries = combinedResults; } else { // 如果获取分享日记失败,只显示我的日记搜索结果 filteredDiaries = myDiaryResults.map(diary => ({ ...diary, type: 'private' })); } } catch (error) { // 如果网络错误,只显示我的日记搜索结果 filteredDiaries = myDiaryResults.map(diary => ({ ...diary, type: 'private' })); } clearBtn.classList.remove('hidden'); displaySearchResults(filteredDiaries); } } // 显示搜索结果(包含我的日记和分享日记) function displaySearchResults(results) { const diaryList = document.getElementById('diaryList'); const searchTerm = document.getElementById('searchInput').value.trim(); if (results.length === 0) { diaryList.innerHTML = '<div class="empty-state">没有找到匹配的日记</div>'; return; } // 按日期分组日记 const groupedDiaries = {}; results.forEach(diary => { const date = new Date(diary.date).toLocaleDateString('zh-CN'); if (!groupedDiaries[date]) { groupedDiaries[date] = []; } groupedDiaries[date].push(diary); }); // 生成HTML let html = ''; const sortedDates = Object.keys(groupedDiaries).sort((a, b) => new Date(b) - new Date(a)); sortedDates.forEach(date => { html += \`<div class="diary-date-group"> <div class="date-header">\${date}</div> <div class="diary-items">\`; groupedDiaries[date].forEach(diary => { // 限制标题显示长度 const maxTitleLength = 15; const displayTitle = diary.title.length > maxTitleLength ? diary.title.substring(0, maxTitleLength) + '...' : diary.title; // 根据类型显示不同的操作按钮 let actionButtons = ''; if (diary.type === 'private') { actionButtons = \` <div class="diary-actions"> <button class="edit-btn" onclick="editDiary(event, '\${diary.id}')" title="编辑">✎</button> <button class="delete-btn" onclick="deleteDiary(event, '\${diary.id}')" title="删除">🗑</button> </div>\`; } else if (diary.type === 'shared') { actionButtons = \` <div class="diary-actions"> <button class="edit-btn" onclick="editSharedDiary(event, '\${diary.id}')" title="编辑">✎</button> <button class="delete-btn" onclick="deleteSharedDiary(event, '\${diary.id}')" title="删除">🗑</button> </div>\`; } // 根据类型设置点击事件 const clickEvent = diary.type === 'private' ? \`onclick="viewDiary('\${diary.id}')"\` : \`onclick="viewSharedDiary('\${diary.shareUrl}')"\`; html += \`<div class="diary-item" \${clickEvent}> <div style="display: flex; justify-content: space-between; align-items: center;"> <div class="diary-title">\${displayTitle} \${diary.type === 'shared' ? '<span style="color: #28a745; font-size: 0.7rem;">[分享]</span>' : ''}</div> \${actionButtons} </div> </div>\`; }); html += \`</div></div>\`; }); diaryList.innerHTML = html; } // 清除搜索 function clearSearch() { document.getElementById('searchInput').value = ''; document.getElementById('clearSearch').classList.add('hidden'); filteredDiaries = [...allDiaries]; displayDiaries(filteredDiaries); } // 加载分享日记列表 async function loadSharedDiaries() { try { const response = await fetch('/api/diary/shares', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); const result = await response.json(); if (result.success) { displaySharedDiaries(result.shares); } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('加载分享列表失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 显示分享日记列表 function displaySharedDiaries(shares) { const shareList = document.getElementById('shareList'); if (shares.length === 0) { shareList.innerHTML = '<div class="empty-state">暂无分享日记</div>'; return; } // 按日期分组分享日记 const groupedShares = {}; shares.forEach(share => { const date = new Date(share.date).toLocaleDateString('zh-CN'); if (!groupedShares[date]) { groupedShares[date] = []; } groupedShares[date].push(share); }); // 生成HTML let html = ''; const sortedDates = Object.keys(groupedShares).sort((a, b) => new Date(b) - new Date(a)); sortedDates.forEach(date => { html += \`<div class="share-date-group"> <div class="date-header">\${date}</div> <div class="share-items">\`; groupedShares[date].forEach(share => { // 限制标题显示长度 const maxTitleLength = 15; const displayTitle = share.title.length > maxTitleLength ? share.title.substring(0, maxTitleLength) + '...' : share.title; html += \`<div class="share-item" onclick="viewSharedDiary('\${share.shareUrl}')"> <div style="display: flex; justify-content: space-between; align-items: center;"> <div class="share-title">\${displayTitle}</div> <div class="share-actions"> <button class="edit-share-btn" onclick="editSharedDiary(event, '\${share.id}')" title="编辑">✎</button> <button class="delete-share-btn" onclick="deleteSharedDiary(event, '\${share.id}')" title="删除">🗑</button> </div> </div> </div>\`; }); html += \`</div></div>\`; }); shareList.innerHTML = html; } // 查看分享日记 function viewSharedDiary(shareUrl) { window.open(shareUrl, '_blank'); } // 编辑分享日记 async function editSharedDiary(event, shareId) { event.stopPropagation(); // 防止触发查看日记 try { // 获取分享日记内容 const response = await fetch(\`/api/share/\${shareId}\`); if (response.ok) { const shareData = await response.json(); // 填充表单(反转义HTML字符) document.getElementById('diaryTitle').value = unescapeHtml(shareData.title); document.getElementById('diaryContent').value = unescapeHtml(shareData.content); // 保存当前编辑的分享ID到表单属性中 document.getElementById('diaryForm').setAttribute('data-edit-share-id', shareId); // 滚动到表单顶部 document.querySelector('.diary-form').scrollIntoView({ behavior: 'smooth' }); showMessage('分享日记已加载到编辑区域,修改后点击分享按钮更新', 'success'); } else { showMessage('加载分享日记失败', 'error'); } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 删除分享日记 async function deleteSharedDiary(event, shareId) { event.stopPropagation(); // 防止触发查看日记 if (!confirm('确定要删除这个分享日记吗?')) { return; } try { const response = await fetch('/api/diary/share', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` }, body: JSON.stringify({ shareId }) }); const result = await response.json(); if (result.success) { showMessage('分享日记删除成功!'); loadSharedDiaries(); // 重新加载分享列表 } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showMessage('删除失败: ' + result.message, 'error'); } } } catch (error) { showMessage('网络错误,请重试', 'error'); } } // 页面加载时检查认证并加载日记和备份 if (checkAuth()) { loadDiaries(); loadBackups(); // 加载备份列表 loadSharedDiaries(); // 加载分享日记列表 } // 添加内容输入事件监听器 document.addEventListener('DOMContentLoaded', function() { const contentTextarea = document.getElementById('diaryContent'); const fontSizeSelect = document.getElementById('fontSizeSelect'); const searchInput = document.getElementById('searchInput'); const clearSearchBtn = document.getElementById('clearSearch'); if (contentTextarea) { contentTextarea.addEventListener('input', updateCharCount); } if (fontSizeSelect) { fontSizeSelect.addEventListener('change', changeFontSize); } // 添加搜索功能事件监听器 if (searchInput) { searchInput.addEventListener('input', performSearch); } if (clearSearchBtn) { clearSearchBtn.addEventListener('click', clearSearch); } // 加载保存的字体大小 loadFontSize(); }); </script> </body> </html>`; } // 日记详情页面 function getDiaryDetailPage(diaryId) { return ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01 Note - 日记详情</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa; min-height: 100vh; } .sites { width:1000px; background:#F2F2F2; border:1px solid rgba(0,0,0,.06); margin:10px auto; padding:0px; color: white; border-radius: 6px; } .sites01 { width:1280px; background:; border:2px solid auto; margin:15px auto; padding:0px; } .sites dl { height:36px; line-height:36px; display:block; margin:0; } .sites dl.alt { background:#c5dff6; border-top:1px solid #ffffff; border-bottom:1px solid #ffffff; } .sites dl.alt2 { background:#dcecfa; border-top:1px solid #ffffff; border-bottom:1px solid #ffffff; } .sites dt,.sites dd { text-align:center; display:block; float:left; } .sites dt { width:60px; } .sites dd { width:90px; margin:0; } .header { background: #D4D4D4; padding: 0.8rem 1.5rem; box-shadow: 0 1px 6px rgba(0,0,0,0.08); display: flex; justify-content: center; align-items: center; position: relative; } .logo { font-size: 1.5rem; font-weight: 400; color: #333; } .back-btn { background: #1C86EE; color: white; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; position: absolute; right: 2.6rem; } .back-btn:hover { background: #1874CD; } .header-controls { display: flex; align-items: center; gap: 0.5rem; } .font-size-select { padding: 0.3rem 0.5rem; border: 1px solid #e1e5e9; border-radius: 4px; font-size: 0.8rem; background: white; cursor: pointer; transition: border-color 0.3s; } .font-size-select:focus { outline: none; border-color: #667eea; } .font-size-select:hover { border-color: #667eea; } .container { max-width: 960px; margin: 1.5rem auto; padding: 0 1.5rem; } .diary-detail { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); max-height: 900px; overflow-y: auto; } .diary-header { border-bottom: 1px solid #e9ecef; padding-bottom: 0.8rem; margin-bottom: 1.2rem; } .diary-title { font-size: 1.5rem; color: #333; margin-bottom: 0.4rem; font-weight: 600; word-wrap: break-word; } .diary-date { color: #666; font-size: 0.8rem; } .diary-content { color: #333; line-height: 1.6; white-space: pre-wrap; font-size: 0.9rem; word-wrap: break-word; overflow-wrap: break-word; word-break: break-all; border: 1px dashed #ccc; padding: 0.8rem; border-radius: 4px; } .loading { text-align: center; padding: 2rem; color: #666; } .error { text-align: center; padding: 2rem; color: #e74c3c; } /* 添加复制按钮样式 */ .copy-btn { background: #007bff; color: white; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; margin-left: 0.8rem; } .copy-btn:hover { background: #0056b3; } .title-container { display: flex; align-items: center; flex-wrap: wrap; } .notification { position: fixed; top: 15px; right: 15px; background: #28a745; color: white; padding: 0.8rem; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.12); display: none; z-index: 1000; font-size: 0.8rem; } </style> </head> <body> <div class="sites"> <div class="header"> <div class="logo">WS01 Note</div> <div class="header-controls"> <select id="fontSizeSelect" class="font-size-select" title="选择字体大小"> <option value="12">12px</option> <option value="14">14px</option> <option value="16" selected>16px</option> <option value="18">18px</option> <option value="20">20px</option> <option value="22">22px</option> </select> <a href="/diary" class="back-btn">← 返回</a> </div> </div> <div class="container"> <div id="diaryDetail" class="diary-detail"> <div class="loading">加载中...</div> </div> </div> <div id="notification" class="notification">内容已复制到剪贴板</div> <script> const diaryId = '${diaryId}'; // 检查登录状态 function checkAuth() { const token = localStorage.getItem('ws01_token'); if (!token) { window.location.href = '/login'; return false; } return true; } // 加载日记详情 async function loadDiaryDetail() { if (!checkAuth()) return; try { const response = await fetch('/api/diary', { headers: { 'Authorization': \`Bearer \${localStorage.getItem('ws01_token')}\` } }); const result = await response.json(); if (result.success) { const diary = result.diaries.find(d => d.id === diaryId); if (diary) { displayDiaryDetail(diary); } else { showError('日记不存在'); } } else { if (result.message === '未授权') { window.location.href = '/login'; } else { showError('加载失败: ' + result.message); } } } catch (error) { showError('网络错误,请重试'); } } // 显示日记详情 function displayDiaryDetail(diary) { const diaryDetail = document.getElementById('diaryDetail'); const date = new Date(diary.date).toLocaleString('zh-CN'); // 限制标题显示长度 const maxTitleLength = 15; const displayTitle = diary.title.length > maxTitleLength ? diary.title.substring(0, maxTitleLength) + '...' : diary.title; diaryDetail.innerHTML = \` <div class="diary-header"> <div class="title-container"> <h1 class="diary-title">\${displayTitle}</h1> <button class="copy-btn" onclick="copyContent('\${diary.content.replace(/'/g, "\\'").replace(/\\n/g, '\\\\n')}')">复制内容</button> </div> <div class="diary-date">\${date}</div> </div> <div class="diary-content" id="diaryContent"></div> \`; // 使用textContent设置内容,避免HTML标签被解析 document.getElementById('diaryContent').textContent = diary.content; // 应用保存的字体大小 loadFontSize(); } // 添加: 字号选择功能 function changeFontSize() { const fontSizeSelect = document.getElementById('fontSizeSelect'); const contentDiv = document.getElementById('diaryContent'); const selectedSize = fontSizeSelect.value; if (contentDiv) { // 应用字体大小到内容区域 contentDiv.style.fontSize = selectedSize + 'px'; // 保存用户选择到本地存储 localStorage.setItem('ws01_detail_font_size', selectedSize); } } // 添加: 加载保存的字体大小 function loadFontSize() { const savedSize = localStorage.getItem('ws01_detail_font_size'); const fontSizeSelect = document.getElementById('fontSizeSelect'); const contentDiv = document.getElementById('diaryContent'); if (savedSize && fontSizeSelect && contentDiv) { fontSizeSelect.value = savedSize; contentDiv.style.fontSize = savedSize + 'px'; } else if (contentDiv) { // 默认字体大小16px contentDiv.style.fontSize = '16px'; } } // 显示错误 function showError(message) { const diaryDetail = document.getElementById('diaryDetail'); diaryDetail.innerHTML = \`<div class="error">\${message}</div>\`; } // 复制内容功能 function copyContent(content) { navigator.clipboard.writeText(content).then(() => { const notification = document.getElementById('notification'); notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 100000); }).catch(err => { console.error('复制失败:', err); const notification = document.getElementById('notification'); notification.textContent = '复制失败'; notification.style.backgroundColor = '#dc3545'; notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; // 恢复默认文本和颜色 notification.textContent = '内容已复制到剪贴板'; notification.style.backgroundColor = '#28a745'; }, 100000); }); } // 复制分享内容功能 function copyShareContent() { if (window.shareContent) { copyContent(window.shareContent); } else { // 如果全局变量不存在,尝试从DOM元素获取 const contentElement = document.getElementById('shareContent'); if (contentElement) { copyContent(contentElement.textContent); } else { console.error('无法获取分享内容'); const notification = document.getElementById('notification'); notification.textContent = '复制失败:无法获取内容'; notification.style.backgroundColor = '#dc3545'; notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 3000); } } } // 页面加载时加载日记详情 loadDiaryDetail(); // 添加字号选择事件监听器 document.addEventListener('DOMContentLoaded', function() { const fontSizeSelect = document.getElementById('fontSizeSelect'); if (fontSizeSelect) { fontSizeSelect.addEventListener('change', changeFontSize); } }); </script> </body> </html>`; } // 分享日记目录页面 function getShareIndexPage() { return ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01 Note - 分享目录</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa; min-height: 100vh; } .sites { width: 1000px; background: #F2F2F2; border: 1px solid rgba(0,0,0,.06); margin: 10px auto; padding: 0px; color: white; border-radius: 6px; } .header { background: #D4D4D4; padding: 0.8rem 1.5rem; box-shadow: 0 1px 6px rgba(0,0,0,0.08); display: flex; justify-content: center; align-items: center; position: relative; } .logo { font-size: 1.5rem; font-weight: 400; color: #333; } .back-btn { background: #1C86EE; color: white; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; position: absolute; right: 2.6rem; text-decoration: none; display: inline-block; } .back-btn:hover { background: #1874CD; } .container { max-width: 960px; margin: 1.5rem auto; padding: 0 1.5rem; } .share-index { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); } .share-index-header { border-bottom: 1px solid #e9ecef; padding-bottom: 0.8rem; margin-bottom: 1.2rem; } .share-index-title { font-size: 1.5rem; color: #333; margin-bottom: 0.4rem; font-weight: 600; } .share-index-subtitle { color: #666; font-size: 0.9rem; } .share-list { display: grid; gap: 1rem; } .share-date-group { margin-bottom: 1.5rem; } .date-header { background: #e9ecef; padding: 0.4rem 0.8rem; font-weight: 600; color: #495057; border-bottom: 1px solid #dee2e6; font-size: 0.9rem; position: sticky; top: 0; z-index: 1; border-radius: 4px 4px 0 0; } .share-items { background: white; border: 1px solid #e9ecef; border-top: none; border-radius: 0 0 4px 4px; } .share-item { padding: 1rem; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background-color 0.2s; display: flex; justify-content: space-between; align-items: center; } .share-item:hover { background-color: #f8f9fa; } .share-item:last-child { border-bottom: none; } .share-item-info { flex: 1; min-width: 0; } .share-item-title { color: #333; font-size: 1rem; font-weight: 500; margin-bottom: 0.3rem; line-height: 1.4; } .share-item-preview { color: #666; font-size: 0.85rem; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 0.3rem; } .share-item-date { color: #999; font-size: 0.75rem; } .share-item-actions { display: flex; gap: 0.5rem; margin-left: 1rem; } .view-btn { background: #007bff; color: white; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; text-decoration: none; display: inline-block; } .view-btn:hover { background: #0056b3; } .loading { text-align: center; padding: 2rem; color: #666; } .error { text-align: center; padding: 2rem; color: #e74c3c; } .empty-state { text-align: center; padding: 3rem; color: #666; } .empty-state-icon { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; } /* 分页控件样式 */ .pagination { display: flex; justify-content: center; align-items: center; margin-top: 2rem; padding: 1rem 0; border-top: 1px solid #e9ecef; } .pagination-info { margin-right: 1rem; color: #666; font-size: 0.9rem; } .pagination-controls { display: flex; gap: 0.5rem; align-items: center; } .pagination-btn { background: #007bff; color: white; border: none; padding: 0.5rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.9rem; transition: background-color 0.2s; } .pagination-btn:hover:not(:disabled) { background: #0056b3; } .pagination-btn:disabled { background: #6c757d; cursor: not-allowed; opacity: 0.6; } .pagination-current { background: #28a745; color: white; border: none; padding: 0.5rem 0.8rem; border-radius: 4px; font-size: 0.9rem; font-weight: 600; } .pagination-jump { display: flex; align-items: center; gap: 0.5rem; margin-left: 1rem; color: blue; } .pagination-jump input { width: 60px; padding: 0.4rem; border: 1px solid #ced4da; border-radius: 4px; text-align: center; font-size: 0.9rem; } .pagination-jump button { background: #6c757d; color: white; border: none; padding: 0.4rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; } .pagination-jump button:hover { background: #5a6268; } .footer { font-size: 14px; color: #292929; margin: 15px auto; text-align: center; } /* 响应式设计 */ @media (max-width: 768px) { .sites { width: 100%; margin: 0; border-radius: 0; } .container { margin: 0.5rem auto; padding: 0 0.5rem; } .share-index { padding: 0.5rem; margin: 0; } .share-item { flex-direction: column; align-items: flex-start; gap: 0.5rem; padding: 0.5rem; } .share-item-info { width: 100%; overflow: hidden; } .share-item-title { font-size: 0.9rem; word-break: break-word; overflow-wrap: break-word; } .share-item-preview { font-size: 0.8rem; -webkit-line-clamp: 3; } .share-item-date { font-size: 0.7rem; } .share-item-actions { margin-left: 0; align-self: flex-end; flex-shrink: 0; } .view-btn { padding: 0.3rem 0.6rem; font-size: 0.7rem; } .pagination { flex-direction: column; gap: 1rem; padding: 0.5rem 0; } .pagination-info { margin-right: 0; margin-bottom: 0.5rem; text-align: center; font-size: 0.8rem; } .pagination-controls { flex-wrap: wrap; justify-content: center; gap: 0.3rem; } .pagination-btn { padding: 0.4rem 0.6rem; font-size: 0.8rem; min-width: 40px; } .pagination-current { padding: 0.4rem 0.6rem; font-size: 0.8rem; min-width: 40px; } .pagination-jump { margin-left: 0; margin-top: 0.5rem; flex-wrap: wrap; justify-content: center; gap: 0.3rem; } .pagination-jump input { width: 50px; padding: 0.3rem; font-size: 0.8rem; } .pagination-jump button { padding: 0.3rem 0.5rem; font-size: 0.7rem; } .pagination-jump span { font-size: 0.8rem; } /* 确保所有元素不会溢出 */ * { max-width: 100%; box-sizing: border-box; } .share-item-title, .share-item-preview { word-break: break-word; overflow-wrap: break-word; hyphens: auto; } .share-date-group { margin-bottom: 1rem; } .date-header { font-size: 0.8rem; padding: 0.3rem 0.5rem; } } </style> </head> <body> <div class="sites"> <div class="header"> <div class="logo">WS01 Note - 分享目录</div> </div> <div class="container"> <div class="share-index"> <div class="share-index-header"> <h1 class="share-index-title">分享文章目录</h1> <p class="share-index-subtitle">这里展示所有公开分享的文章,点击标题可查看完整内容。复制的分享内容可能有改变。</p> </div> <div id="shareList" class="share-list"> <div class="loading">加载中...</div> </div> <div id="pagination" class="pagination" style="display: none;"> <div class="pagination-info" id="paginationInfo"></div> <div class="pagination-controls"> <button class="pagination-btn" id="prevBtn" onclick="changePage(currentPage - 1)">上一页</button> <div id="pageNumbers"></div> <button class="pagination-btn" id="nextBtn" onclick="changePage(currentPage + 1)">下一页</button> </div> <div class="pagination-jump"> <span>跳转到</span> <input type="number" id="jumpInput" min="1" placeholder="">页 <button onclick="jumpToPage()">跳转</button> </div> </div> </div> </div> <div class="footer"> <span id="timeDate">载入天数...</span> <script language="javascript"> var now = new Date(); function createtime(){ var grt= new Date("10/12/2025 00:00:00");/*---这里是网站的启用时间:月日年--*/ now.setTime(now.getTime()+250); days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); document.getElementById("timeDate").innerHTML = "稳定运行"+dnum+"天"; } setInterval("createtime()",250); </script> <span <p> | 本页总访问量 <span id="busuanzi_site_pv"></span> 次 | <a href="/" target="_blank">登录</p></span> <script defer src="https://bsz.211119.xyz/js"></script> <script> // 全局变量 let currentPage = 1; let totalPages = 1; let totalCount = 0; const pageSize = 20; // 从URL获取当前页码 function getCurrentPageFromUrl() { const urlParams = new URLSearchParams(window.location.search); return parseInt(urlParams.get('page')) || 1; } // 更新URL function updateUrl(page) { const url = new URL(window.location); if (page > 1) { url.searchParams.set('page', page); } else { url.searchParams.delete('page'); } window.history.replaceState({}, '', url); } // 加载所有分享日记 async function loadAllShares(page = 1) { try { currentPage = page; const response = await fetch(\`/api/shares?page=\${page}&limit=\${pageSize}\`); if (response.ok) { const result = await response.json(); displayAllShares(result.shares); updatePagination(result.pagination); updateUrl(page); } else { showError('加载失败,请稍后重试'); } } catch (error) { showError('网络错误,请重试'); } } // 显示所有分享日记 function displayAllShares(shares) { const shareList = document.getElementById('shareList'); if (shares.length === 0) { shareList.innerHTML = \` <div class="empty-state"> <div class="empty-state-icon">📝</div> <div>暂无分享日记</div> </div>\`; return; } // 按日期分组分享日记 const groupedShares = {}; shares.forEach(share => { const date = new Date(share.date).toLocaleDateString('zh-CN'); if (!groupedShares[date]) { groupedShares[date] = []; } groupedShares[date].push(share); }); // 生成HTML let html = ''; const sortedDates = Object.keys(groupedShares).sort((a, b) => new Date(b) - new Date(a)); sortedDates.forEach(date => { html += \`<div class="share-date-group"> <div class="date-header">\${date}</div> <div class="share-items">\`; groupedShares[date].forEach(share => { // 生成内容预览(前100个字符) const preview = share.content.length > 100 ? share.content.substring(0, 100) + '...' : share.content; html += \`<div class="share-item" onclick="viewShare('\${share.shareUrl}')"> <div class="share-item-info"> <div class="share-item-title">\${share.title}</div> <div class="share-item-preview">\${preview}</div> <div class="share-item-date">\${new Date(share.date).toLocaleString('zh-CN')}</div> </div> <div class="share-item-actions"> <a href="\${share.shareUrl}" class="view-btn" onclick="event.stopPropagation()">查看</a> </div> </div>\`; }); html += \`</div></div>\`; }); shareList.innerHTML = html; } // 查看分享日记 function viewShare(shareUrl) { window.open(shareUrl, '_blank'); } // 显示错误 function showError(message) { const shareList = document.getElementById('shareList'); shareList.innerHTML = \`<div class="error">\${message}</div>\`; } // 更新分页控件 function updatePagination(pagination) { const paginationDiv = document.getElementById('pagination'); const paginationInfo = document.getElementById('paginationInfo'); const pageNumbers = document.getElementById('pageNumbers'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); currentPage = pagination.currentPage; totalPages = pagination.totalPages; totalCount = pagination.totalCount; // 更新分页信息 const startItem = (currentPage - 1) * pageSize + 1; const endItem = Math.min(currentPage * pageSize, totalCount); paginationInfo.textContent = \`显示 \${startItem}-\${endItem} 条,共 \${totalCount} 条记录\`; // 更新按钮状态 prevBtn.disabled = !pagination.hasPrev; nextBtn.disabled = !pagination.hasNext; // 生成页码按钮 let pageHtml = ''; const maxVisiblePages = 5; let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)); let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); if (endPage - startPage + 1 < maxVisiblePages) { startPage = Math.max(1, endPage - maxVisiblePages + 1); } if (startPage > 1) { pageHtml += \`<button class="pagination-btn" onclick="changePage(1)">1</button>\`; if (startPage > 2) { pageHtml += \`<span style="padding: 0.5rem;">...</span>\`; } } for (let i = startPage; i <= endPage; i++) { if (i === currentPage) { pageHtml += \`<button class="pagination-current">\${i}</button>\`; } else { pageHtml += \`<button class="pagination-btn" onclick="changePage(\${i})">\${i}</button>\`; } } if (endPage < totalPages) { if (endPage < totalPages - 1) { pageHtml += \`<span style="padding: 0.5rem;">...</span>\`; } pageHtml += \`<button class="pagination-btn" onclick="changePage(\${totalPages})">\${totalPages}</button>\`; } pageNumbers.innerHTML = pageHtml; // 显示分页控件 if (totalPages > 1) { paginationDiv.style.display = 'flex'; } else { paginationDiv.style.display = 'none'; } } // 切换页面 function changePage(page) { if (page >= 1 && page <= totalPages && page !== currentPage) { loadAllShares(page); // 滚动到顶部 window.scrollTo({ top: 0, behavior: 'smooth' }); } } // 跳转到指定页面 function jumpToPage() { const jumpInput = document.getElementById('jumpInput'); const page = parseInt(jumpInput.value); if (page >= 1 && page <= totalPages) { changePage(page); jumpInput.value = ''; } else { alert(\`请输入 1 到 \${totalPages} 之间的页码\`); } } // 页面加载时加载所有分享日记 loadAllShares(getCurrentPageFromUrl()); </script> </body> </html>`; } // 分享页面 function getSharePage(shareId) { return ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01 Note - 分享内容</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa; min-height: 100vh; } .sites { width: 1000px; background: #F2F2F2; border: 1px solid rgba(0,0,0,.06); margin: 10px auto; padding: 0px; color: white; border-radius: 6px; } .header { background: #D4D4D4; padding: 0.8rem 1.5rem; box-shadow: 0 1px 6px rgba(0,0,0,0.08); display: flex; justify-content: center; align-items: center; position: relative; } .logo { font-size: 1.5rem; font-weight: 400; color: #333; } .back-btn { background: #1C86EE; color: white; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; position: absolute; right: 2.6rem; text-decoration: none; display: inline-block; } .back-btn:hover { background: #1874CD; } .container { max-width: 960px; margin: 1.5rem auto; padding: 0 1.5rem; } .diary-detail { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); max-height: 900px; overflow-y: auto; } .diary-header { border-bottom: 1px solid #e9ecef; padding-bottom: 0.8rem; margin-bottom: 1.2rem; } .diary-title { font-size: 1.5rem; color: #333; margin-bottom: 0.4rem; font-weight: 600; word-wrap: break-word; } .diary-date { color: #666; font-size: 0.8rem; } .diary-content { color: #333; line-height: 1.6; white-space: pre-wrap; font-size: 0.9rem; word-wrap: break-word; overflow-wrap: break-word; word-break: break-all; border: 1px dashed #ccc; padding: 0.8rem; border-radius: 4px; } .loading { text-align: center; padding: 2rem; color: #666; } .error { text-align: center; padding: 2rem; color: #e74c3c; } .share-notice { background: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 4px; padding: 0.8rem; margin-bottom: 1rem; color: #0066cc; font-size: 0.9rem; } .notification { position: fixed; top: 15px; right: 15px; background: #28a745; color: white; padding: 0.8rem; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.12); display: none; z-index: 1000; font-size: 0.9rem; max-width: 300px; } .footer { font-size: 14px; color: #292929; margin: 15px auto; text-align: center; } /* 响应式设计 */ @media (max-width: 768px) { .sites { width: 95%; margin: 5px auto; } .container { margin: 1rem auto; padding: 0 1rem; } .diary-detail { padding: 1rem; } } </style> </head> <body> <div class="sites"> <div class="header"> <div class="logo">WS01 Note - 分享内容</div> <a href="/share" class="back-btn">← 返回分享目录</a> </div> <div class="container"> <div class="share-notice"> 📢 这是一篇分享的文章,任何人都可以查看 </div> <div id="diaryDetail" class="diary-detail"> <div class="loading">加载中...</div> </div> <!-- 通知元素 --> <div id="notification" class="notification">内容已复制到剪贴板</div> </div> <div class="footer"> <span id="timeDate">载入天数...</span> <script language="javascript"> var now = new Date(); function createtime(){ var grt= new Date("10/12/2025 00:00:00");/*---这里是网站的启用时间:月日年--*/ now.setTime(now.getTime()+250); days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); document.getElementById("timeDate").innerHTML = "稳定运行"+dnum+"天"; } setInterval("createtime()",250); </script> <span <p> | 本页总访问量 <span id="busuanzi_site_pv"></span> 次 | <a href="/" target="_blank">登录</p></span> <script defer src="https://bsz.211119.xyz/js"></script> <script> const shareId = '${shareId}'; // 加载分享日记 async function loadSharedDiary() { try { const response = await fetch(\`/api/share/\${shareId}\`); if (response.ok) { const diary = await response.json(); displaySharedDiary(diary); } else if (response.status === 404) { showError('分享的日记不存在或已被删除'); } else { showError('加载失败,请稍后重试'); } } catch (error) { showError('网络错误,请重试'); } } // 显示分享日记 function displaySharedDiary(diary) { const diaryDetail = document.getElementById('diaryDetail'); const date = new Date(diary.date).toLocaleString('zh-CN'); diaryDetail.innerHTML = \` <div class="diary-header"> <div class="title-container"> <h1 class="diary-title">\${diary.title}</h1> <button class="copy-btn" onclick="copyShareContent()">复制内容</button> </div> <div class="diary-date">\${date}</div> </div> <div class="diary-content" id="shareContent"></div> \`; // 使用textContent设置内容,避免HTML标签被解析 document.getElementById('shareContent').textContent = diary.content; // 将原始内容存储到全局变量中,供复制功能使用 window.shareContent = diary.content; } // 显示错误 function showError(message) { const diaryDetail = document.getElementById('diaryDetail'); diaryDetail.innerHTML = \`<div class="error">\${message}</div>\`; } // 复制内容功能 function copyContent(content) { navigator.clipboard.writeText(content).then(() => { const notification = document.getElementById('notification'); notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 3000); }).catch(err => { console.error('复制失败:', err); const notification = document.getElementById('notification'); notification.textContent = '复制失败'; notification.style.backgroundColor = '#dc3545'; notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; // 恢复默认文本和颜色 notification.textContent = '内容已复制到剪贴板'; notification.style.backgroundColor = '#28a745'; }, 3000); }); } // 复制分享内容功能 function copyShareContent() { if (window.shareContent) { copyContent(window.shareContent); } else { // 如果全局变量不存在,尝试从DOM元素获取 const contentElement = document.getElementById('shareContent'); if (contentElement) { copyContent(contentElement.textContent); } else { console.error('无法获取分享内容'); const notification = document.getElementById('notification'); notification.textContent = '复制失败:无法获取内容'; notification.style.backgroundColor = '#dc3545'; notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 3000); } } } // 页面加载时加载分享日记 loadSharedDiary(); </script> </body> </html>`; } // 处理认证API async function handleAuth(request, env, corsHeaders) { if (request.method !== 'POST') { return new Response('方法不允许', { status: 405, headers: { 'Content-Type': 'text/plain; charset=utf-8', ...corsHeaders } }); } try { const { username, password } = await request.json(); // 验证用户名和密码(从环境变量获取) const validUsername = env.USERNAME || '9527a'; const validPassword = env.PASSWORD || '9527abc'; if (username === validUsername && password === validPassword) { // 生成简单的token(实际应用中应该使用更安全的方法) const token = btoa(username + ':' + Date.now()); return new Response(JSON.stringify({ success: true, token: token, message: '登录成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } else { return new Response(JSON.stringify({ success: false, message: '用户名或密码错误' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } catch (error) { return new Response(JSON.stringify({ success: false, message: '请求格式错误' }), { status: 400, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理日记API async function handleDiaryAPI(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } const token = authHeader.substring(7); try { if (request.method === 'GET') { // 获取日记列表 const diaries = await getDiaries(env); return new Response(JSON.stringify({ success: true, diaries: diaries }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } if (request.method === 'POST') { // 保存新日记 const { title, content, editShareId } = await request.json(); const diary = { id: Date.now().toString(), title: title, content: content, date: new Date().toISOString() }; await saveDiary(env, diary); // 如果是从分享日记编辑而来,需要从分享日记中删除 if (editShareId) { await env.WS01_NOTE_KV.delete(`shared_${editShareId}`); } return new Response(JSON.stringify({ success: true, message: '日记保存成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } // 添加: 处理更新日记 (PUT) if (request.method === 'PUT') { const { id, title, content } = await request.json(); // 获取现有日记 const diaries = await getDiaries(env); const diaryIndex = diaries.findIndex(d => d.id === id); if (diaryIndex === -1) { return new Response(JSON.stringify({ success: false, message: '日记不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } // 更新日记内容 diaries[diaryIndex] = { ...diaries[diaryIndex], title, content, date: new Date().toISOString() // 更新时间 }; await env.WS01_NOTE_KV.put('diaries', JSON.stringify(diaries)); return new Response(JSON.stringify({ success: true, message: '日记更新成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } // 添加: 处理删除日记 (DELETE) if (request.method === 'DELETE') { const { id } = await request.json(); const diaries = await getDiaries(env); const filteredDiaries = diaries.filter(d => d.id !== id); if (filteredDiaries.length === diaries.length) { return new Response(JSON.stringify({ success: false, message: '日记不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } await env.WS01_NOTE_KV.put('diaries', JSON.stringify(filteredDiaries)); return new Response(JSON.stringify({ success: true, message: '日记删除成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } return new Response('方法不允许', { status: 405, headers: { 'Content-Type': 'text/plain; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('日记API错误:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 获取日记列表 async function getDiaries(env) { try { const diariesJson = await env.WS01_NOTE_KV.get('diaries'); if (diariesJson) { const diaries = JSON.parse(diariesJson); return diaries.sort((a, b) => new Date(b.date) - new Date(a.date)); } return []; } catch (error) { console.error('获取日记失败:', error); return []; } } // 保存日记 async function saveDiary(env, diary) { try { const diaries = await getDiaries(env); diaries.unshift(diary); // 添加到开头 // 限制最多保存100篇日记 if (diaries.length > 100) { diaries.splice(100); } await env.WS01_NOTE_KV.put('diaries', JSON.stringify(diaries)); } catch (error) { console.error('保存日记失败:', error); throw error; } } // 处理创建备份 async function handleCreateBackup(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { // 获取当前所有日记 const diaries = await getDiaries(env); // 获取所有分享日记 const listResult = await env.WS01_NOTE_KV.list({ prefix: 'shared_' }); const shares = []; for (const key of listResult.keys) { try { const sharedDiaryJson = await env.WS01_NOTE_KV.get(key.name); if (sharedDiaryJson) { const sharedDiary = JSON.parse(sharedDiaryJson); shares.push(sharedDiary); } } catch (e) { console.error('读取分享日记失败:', e); } } // 创建备份数据 const backup = { id: Date.now().toString(), timestamp: new Date().toISOString(), data: diaries, shares: shares, count: diaries.length, shareCount: shares.length }; // 获取现有的备份列表 let backups = []; try { const backupsJson = await env.WS01_NOTE_KV.get('backups'); if (backupsJson) { backups = JSON.parse(backupsJson); } } catch (e) { console.error('读取备份列表失败:', e); } // 添加新备份到列表开头 backups.unshift(backup); // 限制最多5个备份 if (backups.length > 5) { backups = backups.slice(0, 5); } // 保存备份列表 await env.WS01_NOTE_KV.put('backups', JSON.stringify(backups)); return new Response(JSON.stringify({ success: true, message: '备份创建成功', backupId: backup.id }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('创建备份失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理获取备份列表 async function handleGetBackups(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { // 获取备份列表 let backups = []; try { const backupsJson = await env.WS01_NOTE_KV.get('backups'); if (backupsJson) { backups = JSON.parse(backupsJson); } } catch (e) { console.error('读取备份列表失败:', e); } // 只返回必要的信息 const backupInfo = backups.map(backup => ({ id: backup.id, timestamp: backup.timestamp, count: backup.count, shareCount: backup.shareCount || 0 })); return new Response(JSON.stringify({ success: true, backups: backupInfo }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('获取备份列表失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理恢复备份 async function handleRestoreBackup(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { const { backupId } = await request.json(); // 获取备份列表 let backups = []; try { const backupsJson = await env.WS01_NOTE_KV.get('backups'); if (backupsJson) { backups = JSON.parse(backupsJson); } } catch (e) { console.error('读取备份列表失败:', e); } // 查找指定的备份 const backup = backups.find(b => b.id === backupId); if (!backup) { return new Response(JSON.stringify({ success: false, message: '备份不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } // 恢复个人日记数据 await env.WS01_NOTE_KV.put('diaries', JSON.stringify(backup.data)); // 恢复分享日记数据 if (backup.shares && backup.shares.length > 0) { // 先删除所有现有的分享日记 const existingShares = await env.WS01_NOTE_KV.list({ prefix: 'shared_' }); for (const key of existingShares.keys) { await env.WS01_NOTE_KV.delete(key.name); } // 恢复备份中的分享日记 for (const share of backup.shares) { await env.WS01_NOTE_KV.put(`shared_${share.id}`, JSON.stringify(share)); } } return new Response(JSON.stringify({ success: true, message: '数据恢复成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('恢复备份失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理分享日记 async function handleShareDiary(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { const { title, content, shareId, editDiaryId } = await request.json(); if (request.method === 'PUT' && shareId) { // 更新分享日记 const existingShareJson = await env.WS01_NOTE_KV.get(`shared_${shareId}`); if (!existingShareJson) { return new Response(JSON.stringify({ success: false, message: '分享日记不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } const existingShare = JSON.parse(existingShareJson); const updatedShare = { ...existingShare, title: title, content: content, date: new Date().toISOString() // 更新修改时间 }; await env.WS01_NOTE_KV.put(`shared_${shareId}`, JSON.stringify(updatedShare)); return new Response(JSON.stringify({ success: true, message: '分享日记更新成功', shareId: shareId }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } else { // 创建新分享日记 const newShareId = Date.now().toString() + Math.random().toString(36).substr(2, 9); const sharedDiary = { id: newShareId, title: title, content: content, date: new Date().toISOString(), shared: true }; // 保存到KV存储 await env.WS01_NOTE_KV.put(`shared_${newShareId}`, JSON.stringify(sharedDiary)); // 如果是从我的日记编辑而来,需要从我的日记中删除 if (editDiaryId) { const diaries = await getDiaries(env); const filteredDiaries = diaries.filter(d => d.id !== editDiaryId); await env.WS01_NOTE_KV.put('diaries', JSON.stringify(filteredDiaries)); } return new Response(JSON.stringify({ success: true, message: '分享成功', shareId: newShareId }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } catch (error) { console.error('分享日记失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理获取分享日记 async function handleGetSharedDiary(shareId, env, corsHeaders) { try { // 从KV存储中获取分享日记 const sharedDiaryJson = await env.WS01_NOTE_KV.get(`shared_${shareId}`); if (!sharedDiaryJson) { return new Response(JSON.stringify({ success: false, message: '分享的日记不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } const sharedDiary = JSON.parse(sharedDiaryJson); return new Response(JSON.stringify(sharedDiary), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('获取分享日记失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理获取所有分享日记(公开访问) async function handleGetAllShares(request, env, corsHeaders) { try { // 解析URL参数 const url = new URL(request.url); const page = parseInt(url.searchParams.get('page')) || 1; const limit = parseInt(url.searchParams.get('limit')) || 20; // 获取所有分享日记的键 const listResult = await env.WS01_NOTE_KV.list({ prefix: 'shared_' }); const shares = []; for (const key of listResult.keys) { try { const sharedDiaryJson = await env.WS01_NOTE_KV.get(key.name); if (sharedDiaryJson) { const sharedDiary = JSON.parse(sharedDiaryJson); shares.push({ id: sharedDiary.id, title: sharedDiary.title, content: sharedDiary.content, date: sharedDiary.date, shareUrl: `/share/${sharedDiary.id}` }); } } catch (e) { console.error('解析分享日记失败:', e); } } // 按日期排序(最新的在前) shares.sort((a, b) => new Date(b.date) - new Date(a.date)); // 计算分页 const totalCount = shares.length; const totalPages = Math.ceil(totalCount / limit); const startIndex = (page - 1) * limit; const endIndex = startIndex + limit; const paginatedShares = shares.slice(startIndex, endIndex); return new Response(JSON.stringify({ success: true, shares: paginatedShares, pagination: { currentPage: page, totalPages: totalPages, totalCount: totalCount, limit: limit, hasNext: page < totalPages, hasPrev: page > 1 } }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('获取所有分享日记失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理获取用户分享日记列表 async function handleGetUserShares(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { // 获取所有分享日记的键 const listResult = await env.WS01_NOTE_KV.list({ prefix: 'shared_' }); const shares = []; for (const key of listResult.keys) { try { const sharedDiaryJson = await env.WS01_NOTE_KV.get(key.name); if (sharedDiaryJson) { const sharedDiary = JSON.parse(sharedDiaryJson); shares.push({ id: sharedDiary.id, title: sharedDiary.title, content: sharedDiary.content, date: sharedDiary.date, shareUrl: `${request.url.split('/')[0]}//${request.headers.get('host')}/share/${sharedDiary.id}` }); } } catch (e) { console.error('解析分享日记失败:', e); } } // 按日期排序(最新的在前) shares.sort((a, b) => new Date(b.date) - new Date(a.date)); return new Response(JSON.stringify({ success: true, shares: shares }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('获取分享日记列表失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理删除分享日记 async function handleDeleteShare(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { const { shareId } = await request.json(); // 删除分享日记 await env.WS01_NOTE_KV.delete(`shared_${shareId}`); return new Response(JSON.stringify({ success: true, message: '分享日记删除成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('删除分享日记失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } } // 处理删除备份 async function handleDeleteBackup(request, env, corsHeaders) { // 验证token const authHeader = request.headers.get('Authorization'); if (!authHeader || !authHeader.startsWith('Bearer ')) { return new Response(JSON.stringify({ success: false, message: '未授权' }), { status: 401, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } try { const { backupId } = await request.json(); // 获取备份列表 let backups = []; try { const backupsJson = await env.WS01_NOTE_KV.get('backups'); if (backupsJson) { backups = JSON.parse(backupsJson); } } catch (e) { console.error('读取备份列表失败:', e); } // 过滤掉要删除的备份 const filteredBackups = backups.filter(b => b.id !== backupId); if (filteredBackups.length === backups.length) { return new Response(JSON.stringify({ success: false, message: '备份不存在' }), { status: 404, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } // 保存更新后的备份列表 await env.WS01_NOTE_KV.put('backups', JSON.stringify(filteredBackups)); return new Response(JSON.stringify({ success: true, message: '备份删除成功' }), { headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } catch (error) { console.error('删除备份失败:', error); return new Response(JSON.stringify({ success: false, message: '服务器错误' }), { status: 500, headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders } }); } }
2025年10月19日
1 阅读
0 评论
0 点赞