首页
文章导航
导航
壁纸
更多
留言板
直播
友链
统计
关于
推荐
wszx博客
Search
1
免费二级域名,包括可托管到cf的二级域名
9 阅读
2
网络收集的优选域名列表
9 阅读
3
MoonTV 完整部署教程|免费搭建影视聚合平台!支持 Cloudflare Pages + 自动更新 + 多资源接口
8 阅读
4
serv00注册等系列教程,服务器清理,ssh连接不上问题轻松解决,非挂代理
6 阅读
5
永久免费web主机,轻松建节点,无限流量
4 阅读
默认
日常
学习
技术
登录
Search
标签搜索
cloudflare
白嫖
CF
docker
安装
脚本
壁纸
图片
Linux
Caddy
代码
哪吒
节点
域名
github
搭建
桌面壁纸
手机壁纸
NAT
LXC
ws01
累计撰写
124
篇文章
累计收到
45
条评论
首页
栏目
默认
日常
学习
技术
页面
文章导航
导航
壁纸
留言板
直播
友链
统计
关于
推荐
wszx博客
搜索到
18
篇与
的结果
2025-10-19
基于 Cloudflare Workers AI 的在线文生图/图生图/重绘服务,开箱即用
基于 Cloudflare Workers AI 的在线文生图/图生图/重绘服务,开箱即用项目地址:https://github.com/zhumengkang/cf-ai-image 功能总览 多模型:SDXL、FLUX、DreamShaper、Lightning、SD1.5 图生图、SD1.5 局部重绘一次生成 1–8 张,画廊预览 + 悬浮操作(放大/复制/单张下载)批量下载 ZIP、复制参数、显示每张尺寸与大小真实 it/s 指标(服务端推理耗时),带进度条与 60s 超时提示登录认证(Cookie),支持密码保护、明暗主题、自适应移动端一键部署 1、Cloudflare 控制台 → Workers & Pages → 创建 Worker → 部署。2、绑定 Workers AI:设置 → 绑定 → 添加绑定 → 类型选 “Workers AI”,变量名填 AI → 保存。3、复制代码:将 src/worker.js 与 src/index.html 内容分别放入同名文件,保存并部署。【worker.js文件的第75行是密码,默认密码是admin123】4、可选:设置自定义域(设置 → 域和路由)。配置与自定义 模型清单:编辑 src/worker.js 中 AVAILABLE_MODELS 可增删/改描述、是否需要图片/遮罩。随机提示词:在 RANDOM_PROMPTS 维护。密码:PASSWORDS=['admin123'](留空即无密码),前端含登录遮罩与 Cookie 认证。生成数量:默认开放 1–8,可在前端下拉与后端上限同步调整。常见问题 3001 Unknown internal error:通常为尺寸/步数过大或图片直链不规范。将宽高调到 512–768、步数 < 20;确保 image_url/mask_url 响应头为 image/* 且 ≤10MB。3030 missing mask_image:使用 inpainting 时必须提供 mask_url(已在前端/后端分别做必填校验)。it/s 为什么波动:以服务端推理耗时为准(X-Server-Seconds),网络/解码不会影响该指标。完成后访问 https://..workers.dev/ 即可使用。
2025年10月19日
2 阅读
0 评论
0 点赞
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 点赞
2025-09-03
bpb面板搭建【有时效性,可能1001用不了】
bpb面板搭建 参考 不一样的强哥 创建项目和设置时,不要有敏感词,如vless、trojan等 最新修复BPB Panel失效!混淆代码后的节点高速稳定!CF免费Workers快速部署高速上网,4K起飞!BPB项目:https://github.com/bia-pain-bache/BPB-Worker-PanelProxyIP在线:https://www.nslookup.io/domains/bpb.yousef.isegaro.com/dns-records/大佬分享的部分Proxy_IP域名:bpb.yousef.isegaro.com,ts.hpc.tw,cdn.xn--b6gac.eu.org、cdn-all.xn--b6gac.eu.org、bestproxy.onecf.eu.org、proxyip.cmliussss.net优选IP在线:https://www.wetest.vip/page/cloudflare/address_v4.html https://ipdb.030101.xyz/bestcfv4/ https://stock.hostmonit.com/CloudFlareYes 一、cf项目中设置:1、自定义域2、变量和机密中, UUID 、 TR_PASS 、 PROXY_IP,其中,UUID是设置VLESS节点的 UUID,TR_PASS是设置Trojan节点的密码,PROXY_IP可以先不设置,在BPB面板中设置3、绑定kv,KV 命名空间:kv4、项目中的 最新版本worker.js 代码,更新到版本v3.6.1【2025.10.19】二、面板中设置:1、登录密码【最新几个版本好像无用,第一次打开面板时设置】,设置变量名是 TR_PASS 2、优选域名设置【可以自己找的 优选域名1 , 优选域名2,以下是找好的】:Clean IPs / Domainskk168.wszx.ip-ddns.com cloudflare.182682.xyz freeyx.cloudflare88.eu.org cmcc.090227.xyz www.visa.com.hk bestcf.top cdn.2020111.xyz www.visa.com www.visa.com.sg www.visa.com.tw www.visa.co.jp www.visakorea.com time.is icook.hk icook.tw canva.com envato.com hostinger.com ahrefs.com unpkg.com cf.877774.xyz ct.877774.xyz cmcc.877774.xyz cu.877774.xyz asia.877774.xyz eur.877774.xyz na.877774.xyz www.gco.gov.qa www.gov.se www.gov.ua www.ipget.net www.hugedomains.com shopify.com ip.sb japan.com malaysia.com russia.com singapore.com skk.moe cdn-b100.xn--b6gac.eu.org netlify-cname.xingpingcn.top vercel.001315.xyz vercel-cname.xingpingcn.top cnamefuckxxs.yuchen.icu cf-cname.xingpingcn.top cfcdn.v6.rocks aliyun.2096.us.kg cf.090227.xyz time.cloudflare.com checkout.shopify.com www.digitalocean.com www.csgo.com www.shopify.com www.whoer.net www.whatismyip.com www.udacity.com www.4chan.org www.okcupid.com www.glassdoor.com www.udemy.com www.baipiao.eu.org cdn.anycast.eu.org cdn-all.xn--b6gac.eu.org cdn-b100.xn--b6gac.eu.org xn--b6gac.eu.org edgetunnel.anycast.eu.org alejandracaiccedo.com nc.gocada.co log.bpminecraft.com www.boba88slot.com gur.gov.ua www.zsu.gov.ua www.iakeys.com edtunnel-dgp.pages.dev www.d-555.com fbi.gov *.cloudflare.182682.xyz linux.do 3、没 IPv6 的,登录后关闭 IPv6 4、设置节点类型:rotocols,一般 VLESS 和 Trojan 两种都可以选择5、端口选择:一般 443 就可以了6、proxy IPs规则 Bypass rules ,Bypass LAN 、 Bypass Chin 和 Block Ads 三项打勾7、 Proxy IPs / Domains 中设置: bpb.yousef.isegaro.com 8、 NAT64 Prefixes[2602:fc59:b0:64::] [2a02:898:146:64::] [2602:fc59:11:64::]以上设置完后 “ 确定 ”三、连接 在 Subscriptions - Configs 中
2025年09月03日
2 阅读
0 评论
0 点赞
2025-08-05
Serv00/HostUNO 域名邮箱教程 | 设置全域邮箱(Catch-All) | 提高发件可信度
Serv00/HostUNO 域名邮箱教程 | 设置全域邮箱(Catch-All) | 提高发件可信度 | 第三方工具收发邮件搭建过程 参考视频 一、登录Serv00管理后台,语言设置为中文,方便查看1、点击左侧 邮箱 进入设置2、进入 添加新的电子邮件地址 ,填写 电子邮箱和密码 ,如xxx@你的新域名,如果添加你的edu域名更好,这样完成后就有一个edu邮箱了。3、回到你的域名管理地方, 添加 MX 记录 ,优先事项填入 10 ,内容填入你的 serv00邮件收发地址 【收发地址在你注册邮件中有,类似于mail2.serv00.com】4、【 可不填写,填写后所有这个域名后缀的邮件都转发到这个邮箱 】进入,添加新别名,Domena (Catch-all)电子邮件地址(目标)选择你上一步添加的邮箱,Adres e-mail (docelowy)高级设置中别名类型选择Catch-all5、登录 serv00邮箱后台 ,就可以收发邮件了,而且是任意邮箱前缀@你的新域名,都发送到同一邮箱以上设置已经可以收发邮件了,但是,可能被其它邮箱标记为垃圾邮件或直接拒收,这就必须进行第二步设置,提高该邮箱的可信度 {dotted startColor="#ff6c6c" endColor="#1989fa"/}二、进一步设置1,点击 serv00 左侧的 网站 ,进入 添加新网站 ,把你的域名填写进去2、点击左侧 dns区域,选择刚才添加的域名进入编辑,在你的域名管理地方填入 TXT的值 【类似于 v=spf1 mx a include:mail2.serv00.com -all ,注意不是这个值,域名不同可能值不同】,保证你的域名管理中要 有 MX 和 TXT 两条记录 ,其它记录不填不影响邮箱,填完后,就可以把第1步中填写的域名删除了添加完这一步后,邮件就不会被拒收了,但可能还会进入垃圾邮件,所以还要再进行下一步3、回到左侧的 邮箱 ,在域名列表中,点击域名的 DKIM ,再把该域名的 DNS记录和内容填入你的域名 ,至此你的域名邮箱设置完毕,并且可以收发。
2025年08月05日
2 阅读
0 评论
0 点赞
2025-07-26
Cloudflare Workers + KV 搭建一个带后台管理的blog
本文转载 参考自 一、相关链接: Github原项目 Github修改项目 cf blog是一个运行在cloudflare workers 上的博客程序,使用 cloudflare KV作为数据库,无其他依赖. 兼容静态博客的速度,以及动态博客的灵活性,您可以通过访问项目仓库了解更多详情。二、项目特点:使用workers提供的KV作为数据库,可达到wordpress的灵活性使用cloudflare缓存html来降低KV的读写,使其可达到静态博客的速度后台使用markdown语法,方便快捷,一键发布(页面重构+缓存清理)cfblog-plus新增了 文章置顶、后台首页选择、文章隐藏、静态搜索 等功能三、部署1、注册Cloudflare账号并登录,将域名托管到CloudFlare中获取区域ID:进入域名站点管理页面,页面向下滑动,记录右侧的“区域ID”的值,后续会用到。获取清除缓存 API 令牌:打开 API令牌管理页面 ,点击“创建令牌”按钮,页面拉到最下面,点击“创建自定义令牌”后面的“开始使用”按钮,按以下方式填写。令牌名字:CFBlog-plus 缓存权限:区域 -> 缓存清除 -> 清除 区域资源:包括 -> 特定区域 -> 【您的域名】token留下备用2、新建KV命名空间打开CloudFlare主页,点击左侧的“Workers”,然后点击“KV”项,即可进入“Worker KV”管理页面。在命名空间名称输入框里任意输入一个名称,为了见名知义,这里最好使用CFBLOG作为命名空间名称,点击添加即可。【可能遇到的问题】文章id为ID000nan且访问会报错误,解决方案:在创建的KV中修改SYSTEM_INDEX_NUM的值为03、创建Workers进入Workers 和 Pages 概述,点击“创建应用程序”,点击“创建Workers”,点击“编辑代码”将 index_plus.js 中的内容替换workers的内容【也可以用作者修改好的,后补】。先不修改配置,然后点击“保存并部署”。返回该workers的配置页面,添加“KV 命名空间绑定”,变量名用CFBLOG,下拉选择新添加的KV命名空间,点击“保存按钮”。再将页面滑动上面,点击“快速编辑”,重新打开workers的编辑页面开始添加各类配置。以上配置项均根据配置说明进行配置即可,不再详细说明。要记得“保存并部署”可以点击右侧的“预览”选项卡进行预览,能成功出现页面就说明配置成功了。4、配置域名访问【非必须】保存后可以看到列表里有刚添加的信息,自此,所有配置已经完成,可以愉快的写博客了5、扩展添加评论、阅读量、内容分类、页脚、自定义页面和图标、链接等,请自行发掘6、承载能力:KV基本不存在瓶颈,因为使用了缓存,读写很少唯一瓶颈是 workers的日访问量10w,大约能承受2万IP /日文章数:1G存储空间,几万篇问题不大
2025年07月26日
2 阅读
0 评论
0 点赞
1
2
...
4
您是第
75451
位访客