首页
友链
关于
推荐
wszx博客
Search
1
NAT机商家合集【2024年08月18】
139 阅读
2
常用安装脚本知识24年3月27日更新
134 阅读
3
Linux系统下常用命令【不断补充】
132 阅读
4
注册GCP150刀相关【有手就行】
119 阅读
5
永久免费节点搭建!通过Cloudflare Worker部署免费的VLESS节点,4K高速,解锁Netflix、ChatGPT
115 阅读
默认
日常
学习
技术
登录
Search
标签搜索
cloudflare
壁纸
CF
白嫖
安装
脚本
图片
Linux
docker
域名
桌面壁纸
手机壁纸
NAT
LXC
哪吒
高清壁纸
优选
github
vps
代码
ws01
累计撰写
64
篇文章
累计收到
5
条评论
首页
栏目
默认
日常
学习
技术
页面
友链
关于
推荐
wszx博客
搜索到
9
篇与
的结果
2024-09-08
部署在CF的轻量化导航页面,可移动卡片式书签,方便管理
部署在CF的轻量化导航页面,可移动卡片式书签,方便管理 github项目 Card-Tab 书签卡片式管理,进入管理模式可以自由移动书签位置,添加和删除书签,支持自定义网站分类,支持切换黑暗色主题一、cloudflare workes部署1、原workes, 效果const HTML_CONTENT = `<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Card Tab</title> <style> body { font-family: Arial, sans-serif; // background-color: #f4f4f4; background-color: #d8eac4; margin: 0; padding: 20px; display: flex; flex-direction: column; align-items: center; transition: background-color 0.3s ease; } .card-container { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; } .card { display: flex; flex-direction: column; position: relative; background-color: #a0c9e5; padding: 10px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); cursor: grab; transition: transform 0.2s ease, box-shadow 0.2s ease; width: 200px; height: auto; } .card-top { display: flex; align-items: center; margin-bottom: 5px; } .card-icon { width: 24px; height: 24px; margin-right: 10px; } .card-title { font-size: 16px; font-weight: bold; } .card-url { color: #555; font-size: 12px; word-break: break-all; } .card.dragging { opacity: 0.8; transform: scale(1.05); cursor: grabbing; } .card:hover { transform: translateY(-5px); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); } .delete-btn { position: absolute; top: -10px; right: -10px; background-color: red; color: white; border: none; border-radius: 50%; width: 20px; height: 20px; text-align: center; font-size: 14px; line-height: 20px; cursor: pointer; display: none; } .admin-controls { position: fixed; top: 10px; right: 10px; font-size: 60%; } .admin-controls input { padding: 5px; font-size: 60%; } .admin-controls button { padding: 5px 10px; font-size: 60%; margin-left: 10px; } .add-remove-controls { display: none; margin-top: 10px; } .round-btn { background-color: #007bff; color: white; border: none; border-radius: 50%; width: 40px; height: 40px; text-align: center; font-size: 24px; line-height: 40px; cursor: pointer; margin: 0 10px; } #theme-toggle { position: fixed; bottom: 10px; left: 10px; background-color: #007bff; color: white; border: none; border-radius: 50%; width: 40px; height: 40px; text-align: center; font-size: 24px; line-height: 40px; cursor: pointer; } #dialog-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); justify-content: center; align-items: center; } #dialog-box { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } #dialog-box label { display: block; margin-bottom: 5px; } #dialog-box input, #dialog-box select { width: 100%; padding: 5px; margin-bottom: 10px; } #dialog-box button { padding: 5px 10px; margin-right: 10px; } .section { margin-bottom: 20px; } .section-title { font-size: 24px; font-weight: bold; color: #333; margin-bottom: 10px; } </style> </head> <body> <h1>我的导航</h1> <div class="admin-controls"> <input type="password" id="admin-password" placeholder="输入密码"> <button id="admin-mode-btn" onclick="toggleAdminMode()">进入管理模式</button> </div> <div class="add-remove-controls"> <button class="round-btn" onclick="showAddDialog()">+</button> <button class="round-btn" onclick="toggleRemoveMode()">-</button> </div> <div id="sections-container"> <!-- 分类将在这里动态生成 --> </div> <button id="theme-toggle" onclick="toggleTheme()">◑</button> <div id="dialog-overlay"> <div id="dialog-box"> <label for="name-input">名称</label> <input type="text" id="name-input"> <label for="url-input">地址</label> <input type="text" id="url-input"> <label for="category-select">选择分类</label> <select id="category-select"> <!-- 分类选项将在这里动态生成 --> </select> <button onclick="addLink()">确定</button> <button onclick="hideAddDialog()">取消</button> </div> </div> <div class="copyright"> <!-- 请不要删除 --> <p> 项目地址: <a href="https://github.com/hmhm2022/Card-Tab" target="_blank">GitHub</a> 烦请点个star! </div> <script> let isAdmin = false; let removeMode = false; let isDarkTheme = false; let links = []; const categories = { "常用网站": [], // **编辑自己的网站分类** "工具导航": [], "游戏娱乐": [], "影音视听": [], "技术论坛": [] }; async function loadLinks() { const response = await fetch('/api/getLinks?userId=testUser'); links = await response.json(); Object.keys(categories).forEach(key => { categories[key] = []; }); links.forEach(link => { if (categories[link.category]) { categories[link.category].push(link); } }); loadSections(); updateCategorySelect(); // applyTheme(); } function loadSections() { const container = document.getElementById('sections-container'); container.innerHTML = ''; Object.keys(categories).forEach(category => { const section = document.createElement('div'); section.className = 'section'; const title = document.createElement('div'); title.className = 'section-title'; title.textContent = category; const cardContainer = document.createElement('div'); cardContainer.className = 'card-container'; cardContainer.id = category; section.appendChild(title); section.appendChild(cardContainer); categories[category].forEach(link => { createCard(link, cardContainer); }); container.appendChild(section); }); } function createCard(link, container) { const card = document.createElement('div'); card.className = 'card'; card.setAttribute('draggable', isAdmin); const cardTop = document.createElement('div'); cardTop.className = 'card-top'; const icon = document.createElement('img'); icon.className = 'card-icon'; // icon.src = 'https://www.google.com/s2/favicons?domain=' + link.url; icon.src = 'https://favicon.zhusl.com/ico?url=' + link.url; icon.alt = 'Website Icon'; const title = document.createElement('div'); title.className = 'card-title'; title.textContent = link.name; cardTop.appendChild(icon); cardTop.appendChild(title); const url = document.createElement('div'); url.className = 'card-url'; url.textContent = link.url; card.appendChild(cardTop); card.appendChild(url); // URL 检查和修正 function correctUrl(url) { if (url.startsWith('http://') || url.startsWith('https://')) { return url; } else { return 'http://' + url; } } let correctedUrl = correctUrl(link.url); if (!isAdmin) { card.addEventListener('click', () => { window.open(correctedUrl, '_blank'); }); } const deleteBtn = document.createElement('button'); deleteBtn.textContent = '–'; deleteBtn.className = 'delete-btn'; deleteBtn.onclick = function (event) { event.stopPropagation(); removeCard(card); }; card.appendChild(deleteBtn); if (isDarkTheme) { card.style.backgroundColor = '#1e1e1e'; card.style.color = '#ffffff'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)'; } else { card.style.backgroundColor = '#a0c9e5'; card.style.color = '#333'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; } card.addEventListener('dragstart', dragStart); card.addEventListener('dragover', dragOver); card.addEventListener('dragend', dragEnd); card.addEventListener('drop', drop); if (isAdmin && removeMode) { deleteBtn.style.display = 'block'; } container.appendChild(card); } function updateCategorySelect() { const categorySelect = document.getElementById('category-select'); categorySelect.innerHTML = ''; Object.keys(categories).forEach(category => { const option = document.createElement('option'); option.value = category; option.textContent = category; categorySelect.appendChild(option); }); } async function saveLinks() { let links = []; for (const category in categories) { links = links.concat(categories[category]); } await fetch('/api/saveOrder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'testUser', links }), }); } function addLink() { const name = document.getElementById('name-input').value; const url = document.getElementById('url-input').value; const category = document.getElementById('category-select').value; if (name && url && category) { const newLink = { name, url, category }; if (!categories[category]) { categories[category] = []; } categories[category].push(newLink); const container = document.getElementById(category); createCard(newLink, container); saveLinks(); document.getElementById('name-input').value = ''; document.getElementById('url-input').value = ''; hideAddDialog(); } } function removeCard(card) { const url = card.querySelector('.card-url').textContent; let category; for (const key in categories) { const index = categories[key].findIndex(link => link.url === url); if (index !== -1) { categories[key].splice(index, 1); category = key; break; } } card.remove(); saveLinks(); } let draggedCard = null; function dragStart(event) { if (!isAdmin) return; draggedCard = event.target; draggedCard.classList.add('dragging'); event.dataTransfer.effectAllowed = "move"; } function dragOver(event) { if (!isAdmin) return; event.preventDefault(); const target = event.target.closest('.card'); if (target && target !== draggedCard) { const container = target.parentElement; const mousePositionX = event.clientX; const targetRect = target.getBoundingClientRect(); if (mousePositionX < targetRect.left + targetRect.width / 2) { container.insertBefore(draggedCard, target); } else { container.insertBefore(draggedCard, target.nextSibling); } } } function drop(event) { if (!isAdmin) return; event.preventDefault(); draggedCard.classList.remove('dragging'); draggedCard = null; saveCardOrder(); } // function dragEnd(event) { // draggedCard.classList.remove('dragging'); function dragEnd(event) { if (draggedCard) { draggedCard.classList.remove('dragging'); } } async function saveCardOrder() { if (!isAdmin) return; const containers = document.querySelectorAll('.card-container'); let newLinks = []; containers.forEach(container => { const category = container.id; categories[category] = []; [...container.children].forEach(card => { const url = card.querySelector('.card-url').textContent; const name = card.querySelector('.card-title').textContent; const link = { name, url, category }; categories[category].push(link); newLinks.push(link); }); }); links = newLinks; await fetch('/api/saveOrder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'testUser', links: newLinks }), }); } function toggleAdminMode() { const passwordInput = document.getElementById('admin-password'); const adminBtn = document.getElementById('admin-mode-btn'); const addRemoveControls = document.querySelector('.add-remove-controls'); if (!isAdmin) { verifyPassword(passwordInput.value) .then(isValid => { if (isValid) { isAdmin = true; adminBtn.textContent = "退出管理模式"; alert('已进入管理模式'); addRemoveControls.style.display = 'block'; reloadCardsAsAdmin(); } else { alert('密码错误'); } }); } else { isAdmin = false; removeMode = false; adminBtn.textContent = "进入管理模式"; alert('已退出管理模式'); addRemoveControls.style.display = 'none'; const deleteButtons = document.querySelectorAll('.delete-btn'); deleteButtons.forEach(btn => btn.style.display = 'none'); reloadCardsAsAdmin(); } passwordInput.value = ''; } function reloadCardsAsAdmin() { document.querySelectorAll('.card-container').forEach(container => { container.innerHTML = ''; }); loadLinks().then(() => { if (isDarkTheme) { applyDarkTheme(); } }); } function applyDarkTheme() { document.body.style.backgroundColor = '#121212'; document.body.style.color = '#ffffff'; const cards = document.querySelectorAll('.card'); cards.forEach(card => { card.style.backgroundColor = '#1e1e1e'; card.style.color = '#ffffff'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)'; }); } function showAddDialog() { document.getElementById('dialog-overlay').style.display = 'flex'; } function hideAddDialog() { document.getElementById('dialog-overlay').style.display = 'none'; } function toggleRemoveMode() { removeMode = !removeMode; const deleteButtons = document.querySelectorAll('.delete-btn'); deleteButtons.forEach(btn => { btn.style.display = removeMode ? 'block' : 'none'; }); } function toggleTheme() { isDarkTheme = !isDarkTheme; // 设置暗色主题和亮色主题的背景色 document.body.style.backgroundColor = isDarkTheme ? '#121212' : '#d8eac4'; // 设置暗色主题和亮色主题的文本颜色 document.body.style.color = isDarkTheme ? '#ffffff' : '#333'; const cards = document.querySelectorAll('.card'); cards.forEach(card => { // 卡片背景和文本颜色设置 card.style.backgroundColor = isDarkTheme ? '#1e1e1e' : '#a0c9e5'; card.style.color = isDarkTheme ? '#ffffff' : '#333'; // 卡片阴影的设置,增强暗色主题的阴影 card.style.boxShadow = isDarkTheme ? '0 4px 8px rgba(0, 0, 0, 0.5)' : '0 4px 8px rgba(0, 0, 0, 0.1)'; }); const dialogBox = document.getElementById('dialog-box'); // 对话框背景和文本颜色设置 dialogBox.style.backgroundColor = isDarkTheme ? '#1e1e1e' : '#ffffff'; dialogBox.style.color = isDarkTheme ? '#ffffff' : '#333'; const inputs = dialogBox.querySelectorAll('input, select'); inputs.forEach(input => { // 输入框背景和文本颜色设置 input.style.backgroundColor = isDarkTheme ? '#333333' : '#ffffff'; input.style.color = isDarkTheme ? '#ffffff' : '#333'; }); } async function verifyPassword(inputPassword) { const response = await fetch('/api/verifyPassword', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: inputPassword }), }); const result = await response.json(); return result.valid; } loadLinks(); </script> </body> </html> `; export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === '/') { return new Response(HTML_CONTENT, { headers: { 'Content-Type': 'text/html' } }); } if (url.pathname === '/api/getLinks') { const userId = url.searchParams.get('userId'); const links = await env.CARD_ORDER.get(userId); return new Response(links || JSON.stringify([]), { status: 200 }); } if (url.pathname === '/api/saveOrder' && request.method === 'POST') { const { userId, links } = await request.json(); await env.CARD_ORDER.put(userId, JSON.stringify(links)); return new Response(JSON.stringify({ success: true }), { status: 200 }); } if (url.pathname === '/api/verifyPassword' && request.method === 'POST') { const { password } = await request.json(); const isValid = password === env.ADMIN_PASSWORD; // 从环境变量中获取密码 return new Response(JSON.stringify({ valid: isValid }), { status: isValid ? 200 : 403, headers: { 'Content-Type': 'application/json' }, }); } return new Response('Not Found', { status: 404 }); } };2、修改后的workes, 效果const HTML_CONTENT = `<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WS01の主页</title> <style> body { font-family: Arial, sans-serif; // background-color: #f4f4f4; background-color: #d4d4d4; margin: 0; padding: 20px; display: flex; flex-direction: column; align-items: center; transition: background-color 0.3s ease; } ul { padding: 0; margin-block-start: 1em; margin-block-end: 1em; margin-inline-start: 0px; margin-inline-end: 0px; padding-inline-start: 40px; unicode-bidi: isolate; } li { display: list-item; text-align: -webkit-match-parent; unicode-bidi: isolate; margin: 0 8px; } .background { background-image: linear-gradient(#d4d4d4 1px, transparent 0), linear-gradient(90deg, #d4d4d4 1px, transparent 0); background-size: 32px 32px; background-color: #fffcf8; } .header { background-color: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, .1); transition: background-color .5s; } .navbar { display: flex; width: 1280px; margin: auto; height: 40px; } .navbar .brand { display: flex; align-items: center; color: #555; } .brand .logo { max-width: 36px; } .brand .title { margin-left: 5px; font-family: helvetica neue, helvetica, arial, sans-serif; font-size: 24px; font-weight: 700; } .beta { color: #ccc; font-size: 11px; font-weight: 400; position: relative; top: -14px; } .category-list { list-style: none; display: flex; align-items: center; } .sites { width:1280px; background:; border:2px solid auto; margin:15px auto; padding:0px; text-align:center; } .sites1 { 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:#d4d4d4; border-top:1px solid #ffffff; border-bottom:1px solid #ffffff; } .sites dl.alt2 { background:#d4d4d4; 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; } .footer { width:580px; text-align:center; margin:5px auto; padding:2px; } .card-container { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; width: 100%; /* 宽度设为100%,以适应不同设备 */ } .card { display: flex; flex-direction: column; position: relative; background-color: #d4d4d4; padding: 10px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); cursor: grab; transition: transform 0.2s ease, box-shadow 0.2s ease; word-break: break-word; /* 防止超出边界 */ } /* 中等屏幕 (平板等) */ @media (max-width: 1024px) { .card-container { grid-template-columns: repeat(8, 1fr); /* 中等屏幕显示4列 */ } } /* 小屏幕 (手机) */ @media (max-width: 768px) { .card-container { grid-template-columns: repeat(6, 1fr); /* 小屏幕显示2列 */ } .card { padding: 8px; /* 减小卡片内边距 */ font-size: 0.9rem; /* 调整字体大小0.9 */ } .admin-controls { top: 5px; right: 5px; } .round-btn, #theme-toggle { width: 30px; height: 30px; font-size: 18px; line-height: 30px; } } /* 超小屏幕 (更小手机) */ @media (max-width: 480px) { .card-container { grid-template-columns: repeat(5, 1fr); /* 超小屏幕显示1列 */ } .card { padding: 6px; /* 进一步减小卡片内边距5 */ font-size: 0.8rem; /* 再次缩小字体0.8 */ } .round-btn, #theme-toggle { width: 25px; height: 25px; font-size: 16px; line-height: 25px; } } .card-top { display: flex; align-items: center; margin-bottom: 5px; } .card-icon { width: 20px; height: 20px; margin-right: 8px; } .card-title { font-size: 16px; font-weight: bold; } .card-url { color: #555; font-size: 12px; word-break: break-all; } .card.dragging { opacity: 0.8; transform: scale(1.05); cursor: grabbing; } .card:hover { transform: translateY(-5px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .delete-btn { position: absolute; top: -10px; right: -10px; background-color: red; color: white; border: none; border-radius: 50%; width: 20px; height: 20px; text-align: center; font-size: 14px; line-height: 20px; cursor: pointer; display: none; } .admin-controls { position: fixed; top: 10px; right: 10px; font-size: 60%; } .admin-controls input { padding: 5px; font-size: 60%; } .admin-controls button { padding: 5px 10px; font-size: 60%; margin-left: 10px; } .add-remove-controls { display: none; margin-top: 10px; } .round-btn { background-color: #007bff; color: white; border: none; border-radius: 50%; width: 40px; height: 40px; text-align: center; font-size: 24px; line-height: 40px; cursor: pointer; margin: 0 10px; } #theme-toggle { position: fixed; bottom: 10px; left: 10px; background-color: #007bff; color: white; border: none; border-radius: 50%; width: 40px; height: 40px; text-align: center; font-size: 24px; line-height: 40px; cursor: pointer; } #dialog-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); justify-content: center; align-items: center; } #dialog-box { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } #dialog-box label { display: block; margin-bottom: 5px; } #dialog-box input, #dialog-box select { width: 100%; padding: 5px; margin-bottom: 10px; } #dialog-box button { padding: 5px 10px; margin-right: 10px; } .section { margin-bottom: 20px; } .section-title { font-size: 24px; font-weight: bold; color: #333; margin-bottom: 10px; } </style> </head> <body class="background"> </div> <div class="sites"> <!-- 00 --> <header class="header"> <nav class="navbar"> <a href="https://ddo.us.kg/" class="brand"> <img class="debug logo" src="https://cdn.glitch.global/efdace30-a873-49c7-aaa9-4fa31679ee0c/thumbnails%2F%E5%9B%BE%E6%A0%8701.jpg?1692046715299"> <span class="debug title">WS01の主页</span> <span class="debug beta">beta</span> </a> </header> </div> </div> <div class="add-remove-controls"> <button class="round-btn" onclick="showAddDialog()">+</button> <button class="round-btn" onclick="toggleRemoveMode()">-</button> </div> <div class="sites1"> <!-- 00 --> <div id="sections-container"> <!-- 分类将在这里动态生成 --> </div> <button id="theme-toggle" onclick="toggleTheme()">◑</button> <div id="dialog-overlay"> <div id="dialog-box"> <label for="name-input">名称</label> <input type="text" id="name-input"> <label for="url-input">地址</label> <input type="text" id="url-input"> <label for="category-select">选择分类</label> <select id="category-select"> <!-- 分类选项将在这里动态生成 --> </select> <button onclick="addLink()">确定</button> <button onclick="hideAddDialog()">取消</button> </div> </div> <div class="copyright"> <!-- 请不要删除 --> <br /> <!-- body 页脚 --> <div class="footer"> <input type="password" id="admin-password" placeholder="输入密码"> <button id="admin-mode-btn" onclick="toggleAdminMode()">进入管理模式</button> <p> <a href="https://github.com/hmhm2022/Card-Tab" target="_blank">GitHub</a> 项目 <!-- 开站时间开始 --> <span id="timeDate">载入天数...</span><span id="times">载入时分秒...</span> <script language="javascript"> var now = new Date(); function createtime(){ var grt= new Date("09/05/2024 00:00:00");/*---这里是网站的启用时间--*/ now.setTime(now.getTime()+250); days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours); if(String(hnum).length ==1 ){hnum = "0" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum); mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = "0" + mnum;} seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum); snum = Math.round(seconds); if(String(snum).length ==1 ){snum = "0" + snum;} document.getElementById("timeDate").innerHTML = "稳定运行"+dnum+"天"; document.getElementById("times").innerHTML = hnum + "小时" + mnum + "分" + snum + "秒"; } setInterval("createtime()",250); </script> <!-- 开站时间结束 --> </div> <script> let isAdmin = false; let removeMode = false; let isDarkTheme = false; let links = []; const categories = { "常·用": [], // **编辑自己的网站分类** "工·具": [], "影·音": [], "N·B·A": [], "论·坛": [], "主·页": [], "玩·具": [], "v·p·s": [], "下·载": [], "商·城": [], "搜·译": [], "学·习": [], "其·它": [] }; async function loadLinks() { const response = await fetch('/api/getLinks?userId=testUser'); links = await response.json(); Object.keys(categories).forEach(key => { categories[key] = []; }); links.forEach(link => { if (categories[link.category]) { categories[link.category].push(link); } }); loadSections(); updateCategorySelect(); // applyTheme(); } function loadSections() { const container = document.getElementById('sections-container'); container.innerHTML = ''; Object.keys(categories).forEach(category => { const section = document.createElement('div'); section.className = 'section'; const title = document.createElement('div'); title.className = 'section-title'; title.textContent = category; const cardContainer = document.createElement('div'); cardContainer.className = 'card-container'; cardContainer.id = category; section.appendChild(title); section.appendChild(cardContainer); categories[category].forEach(link => { createCard(link, cardContainer); }); container.appendChild(section); }); } function createCard(link, container) { const card = document.createElement('div'); card.className = 'card'; card.setAttribute('draggable', isAdmin); const cardTop = document.createElement('div'); cardTop.className = 'card-top'; const icon = document.createElement('img'); icon.className = 'card-icon'; // icon.src = 'https://www.google.com/s2/favicons?domain=' + link.url; icon.src = 'https://favicon.zhusl.com/ico?url=' + link.url; icon.alt = 'Website Icon'; const title = document.createElement('div'); title.className = 'card-title'; title.textContent = link.name; cardTop.appendChild(icon); cardTop.appendChild(title); const url = document.createElement('div'); url.className = 'card-url'; url.textContent = link.url; card.appendChild(cardTop); card.appendChild(url); // URL 检查和修正 function correctUrl(url) { if (url.startsWith('http://') || url.startsWith('https://')) { return url; } else { return 'http://' + url; } } let correctedUrl = correctUrl(link.url); if (!isAdmin) { card.addEventListener('click', () => { window.open(correctedUrl, '_blank'); }); } const deleteBtn = document.createElement('button'); deleteBtn.textContent = '–'; deleteBtn.className = 'delete-btn'; deleteBtn.onclick = function (event) { event.stopPropagation(); removeCard(card); }; card.appendChild(deleteBtn); if (isDarkTheme) { card.style.backgroundColor = '#1e1e1e'; card.style.color = '#ffffff'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)'; } else { card.style.backgroundColor = '#d4d4d4'; card.style.color = '#333'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; } card.addEventListener('dragstart', dragStart); card.addEventListener('dragover', dragOver); card.addEventListener('dragend', dragEnd); card.addEventListener('drop', drop); if (isAdmin && removeMode) { deleteBtn.style.display = 'block'; } container.appendChild(card); } function updateCategorySelect() { const categorySelect = document.getElementById('category-select'); categorySelect.innerHTML = ''; Object.keys(categories).forEach(category => { const option = document.createElement('option'); option.value = category; option.textContent = category; categorySelect.appendChild(option); }); } async function saveLinks() { let links = []; for (const category in categories) { links = links.concat(categories[category]); } await fetch('/api/saveOrder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'testUser', links }), }); } function addLink() { const name = document.getElementById('name-input').value; const url = document.getElementById('url-input').value; const category = document.getElementById('category-select').value; if (name && url && category) { const newLink = { name, url, category }; if (!categories[category]) { categories[category] = []; } categories[category].push(newLink); const container = document.getElementById(category); createCard(newLink, container); saveLinks(); document.getElementById('name-input').value = ''; document.getElementById('url-input').value = ''; hideAddDialog(); } } function removeCard(card) { const url = card.querySelector('.card-url').textContent; let category; for (const key in categories) { const index = categories[key].findIndex(link => link.url === url); if (index !== -1) { categories[key].splice(index, 1); category = key; break; } } card.remove(); saveLinks(); } let draggedCard = null; function dragStart(event) { if (!isAdmin) return; draggedCard = event.target; draggedCard.classList.add('dragging'); event.dataTransfer.effectAllowed = "move"; } function dragOver(event) { if (!isAdmin) return; event.preventDefault(); const target = event.target.closest('.card'); if (target && target !== draggedCard) { const container = target.parentElement; const mousePositionX = event.clientX; const targetRect = target.getBoundingClientRect(); if (mousePositionX < targetRect.left + targetRect.width / 2) { container.insertBefore(draggedCard, target); } else { container.insertBefore(draggedCard, target.nextSibling); } } } function drop(event) { if (!isAdmin) return; event.preventDefault(); draggedCard.classList.remove('dragging'); draggedCard = null; saveCardOrder(); } // function dragEnd(event) { // draggedCard.classList.remove('dragging'); function dragEnd(event) { if (draggedCard) { draggedCard.classList.remove('dragging'); } } async function saveCardOrder() { if (!isAdmin) return; const containers = document.querySelectorAll('.card-container'); let newLinks = []; containers.forEach(container => { const category = container.id; categories[category] = []; [...container.children].forEach(card => { const url = card.querySelector('.card-url').textContent; const name = card.querySelector('.card-title').textContent; const link = { name, url, category }; categories[category].push(link); newLinks.push(link); }); }); links = newLinks; await fetch('/api/saveOrder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'testUser', links: newLinks }), }); } function toggleAdminMode() { const passwordInput = document.getElementById('admin-password'); const adminBtn = document.getElementById('admin-mode-btn'); const addRemoveControls = document.querySelector('.add-remove-controls'); if (!isAdmin) { verifyPassword(passwordInput.value) .then(isValid => { if (isValid) { isAdmin = true; adminBtn.textContent = "退出管理模式"; alert('已进入管理模式'); addRemoveControls.style.display = 'block'; reloadCardsAsAdmin(); } else { alert('密码错误'); } }); } else { isAdmin = false; removeMode = false; adminBtn.textContent = "进入管理模式"; alert('已退出管理模式'); addRemoveControls.style.display = 'none'; const deleteButtons = document.querySelectorAll('.delete-btn'); deleteButtons.forEach(btn => btn.style.display = 'none'); reloadCardsAsAdmin(); } passwordInput.value = ''; } function reloadCardsAsAdmin() { document.querySelectorAll('.card-container').forEach(container => { container.innerHTML = ''; }); loadLinks().then(() => { if (isDarkTheme) { applyDarkTheme(); } }); } function applyDarkTheme() { document.body.style.backgroundColor = '#121212'; document.body.style.color = '#ffffff'; const cards = document.querySelectorAll('.card'); cards.forEach(card => { card.style.backgroundColor = '#1e1e1e'; card.style.color = '#ffffff'; card.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)'; }); } function showAddDialog() { document.getElementById('dialog-overlay').style.display = 'flex'; } function hideAddDialog() { document.getElementById('dialog-overlay').style.display = 'none'; } function toggleRemoveMode() { removeMode = !removeMode; const deleteButtons = document.querySelectorAll('.delete-btn'); deleteButtons.forEach(btn => { btn.style.display = removeMode ? 'block' : 'none'; }); } function toggleTheme() { isDarkTheme = !isDarkTheme; // 设置暗色主题和亮色主题的背景色 document.body.style.backgroundColor = isDarkTheme ? '#696969' : '#FFFFFF'; // 设置暗色主题和亮色主题的文本颜色 document.body.style.color = isDarkTheme ? '#ffffff' : '#333'; const cards = document.querySelectorAll('.card'); cards.forEach(card => { // 卡片背景和文本颜色设置 card.style.backgroundColor = isDarkTheme ? '#1e1e1e' : '#d4d4d4'; card.style.color = isDarkTheme ? '#ffffff' : '#333'; // 卡片阴影的设置,增强暗色主题的阴影 card.style.boxShadow = isDarkTheme ? '0 4px 8px rgba(0, 0, 0, 0.5)' : '0 4px 8px rgba(0, 0, 0, 0.1)'; }); const dialogBox = document.getElementById('dialog-box'); // 对话框背景和文本颜色设置 dialogBox.style.backgroundColor = isDarkTheme ? '#1e1e1e' : '#ffffff'; dialogBox.style.color = isDarkTheme ? '#ffffff' : '#333'; const inputs = dialogBox.querySelectorAll('input, select'); inputs.forEach(input => { // 输入框背景和文本颜色设置 input.style.backgroundColor = isDarkTheme ? '#333333' : '#ffffff'; input.style.color = isDarkTheme ? '#ffffff' : '#333'; }); } async function verifyPassword(inputPassword) { const response = await fetch('/api/verifyPassword', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: inputPassword }), }); const result = await response.json(); return result.valid; } loadLinks(); </script> </body> </html> `; export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === '/') { return new Response(HTML_CONTENT, { headers: { 'Content-Type': 'text/html' } }); } if (url.pathname === '/api/getLinks') { const userId = url.searchParams.get('userId'); const links = await env.CARD_ORDER.get(userId); return new Response(links || JSON.stringify([]), { status: 200 }); } if (url.pathname === '/api/saveOrder' && request.method === 'POST') { const { userId, links } = await request.json(); await env.CARD_ORDER.put(userId, JSON.stringify(links)); return new Response(JSON.stringify({ success: true }), { status: 200 }); } if (url.pathname === '/api/verifyPassword' && request.method === 'POST') { const { password } = await request.json(); const isValid = password === env.ADMIN_PASSWORD; // 从环境变量中获取密码 return new Response(JSON.stringify({ valid: isValid }), { status: isValid ? 200 : 403, headers: { 'Content-Type': 'application/json' }, }); } return new Response('Not Found', { status: 404 }); } };二、添加变量1、先建立一个kv空间,名字为: CARD_ORDER ,再在 变量-KV 命名空间绑定 刚才建立的kv空间。2、变量-环境变量 中添加 ADMIN_PASSWORD ,值是你的后台管理员密码3、有域名的可邦定域名,完成。
2024年09月08日
2 阅读
0 评论
0 点赞
2024-07-24
一次可生成n加节点,YTB视频4K秒开!2024最新永久免费,订阅节点订阅搭建保姆教程
一次可生成n加节点,YTB视频4K秒开!2024最新永久免费,订阅节点订阅搭建保姆教程 本文转自: 科技共享 原项目地址 代码 创建邦定KV空间,命名:settings支持Windows、MacOS、Linux 优选IP使用,操作简单,速度快。创建好后, 第一次打开设置登录密码 第一次打开设置登录密码 第一次打开设置登录密码 其它根据需要再设置
2024年07月24日
109 阅读
0 评论
0 点赞
2024-07-19
永久免费节点搭建!通过Cloudflare Worker部署免费的VLESS节点,4K高速,解锁Netflix、ChatGPT
永久免费节点搭建!通过Cloudflare Worker部署免费的VLESS节点,4K高速,解锁Netflix、ChatGPT本文转载自: 科技小露 本文是通过 zizifn 大佬的一个开源项目 edgetunnel ,使得我们可以免费的在 Cloudflare 上面通过部署 Worker ,来创建一个免费 VLESS 节点!一、准备工作注册 Cloudflare 账号并登录二、CloudFlare 部署免费节点原作者 GitHub 源项目地址 Worker 部署 VLESS1、来到 Cloudflare 首页,点击 Workers 和 Pages ,创建 Work ,自定义名称,然后部署!而后,编辑代码,清除原先代码,填入如下代码: Workers代码 2、在线生成一个 UUID ,用于替换下面代码中第七行的 UUID。(或是用 V2rayN 生成一个)3、因为原项目里面的代码“ proxy IP ”为空,关于 proxy IP ,是用于转发CF的一些流量,所以,若是存在套了CF的一些网站无法打开,请更换其中的其他网址,也就是第九行中的部分网址!ProxyIP 可以替换成域名或优选IPproxyip.us.fxxk.dedyn.io更多 proxyIP 列表:CM 维护proxyip.us.fxxk.dedyn.io IP落地区域: 美国 维护频率: 12小时/次 proxyip.sg.fxxk.dedyn.io IP落地区域: 新加坡 维护频率: 12小时/次 proxyip.jp.fxxk.dedyn.io IP落地区域: 日本 维护频率: 12小时/次 proxyip.hk.fxxk.dedyn.io IP落地区域: 香港 维护频率: 12小时/次 proxyip.aliyun.fxxk.dedyn.io IP落地区域: 阿里云 维护频率: 4小时/次 proxyip.oracle.fxxk.dedyn.io IP落地区域: 甲骨文 维护频率: 4小时/次 proxyip.digitalocean.fxxk.dedyn.io IP落地区域: 数码海 维护频率: 4小时/次白嫖哥维护workers.cloudflare.cyouMingyu 维护my-telegram-is-herocore.onecf.eu.org sg.ipdb.rr.nu nl.ipdb.rr.nu hk.ipdb.rr.nu jp.ipdb.rr.nu us.ipdb.rr.nu小一维护hk.cf.zhetengsha.eu.org sg.cf.zhetengsha.eu.org us.cf.zhetengsha.eu.org jp.cf.zhetengsha.eu.org代码修改完毕以后,点击右边的部署 – 保存并部署,然后点击左边的箭头,返回! 4、访问创建的vlessjd,在网站后加上“/UUID” ,即可看到节点信息,当然,里面包含了 订阅链接! 至此,节点部署完毕,复制 VLESS 的订阅链接,粘贴到 V2rayN 里面。其它使用和优选等自己找教程
2024年07月19日
115 阅读
0 评论
0 点赞
2024-07-18
使用cloudflare搭建永久免费Trojan节点,免费创建自定义域名,多个节点,无需手动优选,免费无门槛搭建
使用cloudflare搭建永久免费Trojan节点,免费创建自定义域名,多个节点,无需手动优选,免费无门槛搭建本文转载自: 小白科技 之前小白分享过很多期使用cloudflare自建VLESS节点,但是cloudflare的规则跟新之后导致很多VLESS节点都无法使用,但是Trojan的节点是可以使用的,所以本期视频我们通过cloudflare的Workers搭建Trojan节点。首先说明,本期视频使用的所有代码都是CM大佬发布在Github上的免费开源项目,在这里感谢大佬。源项目地址:https://github.com/cmliu/epeius?tab=readme-ov-file一、项目部署:cloudflare官网地址:https://dash.cloudflare.com/sign-up项目代码地址:https://github.com/cmliu/epeius/blob/main/_worker.js项目部署完毕后添加变量1、PASSWORD,密码:个人喜好可取任意数字2、SUB,trojan.fxxk.dedyn.io二、自定义域名:自己解决,设置完这两步就可以正常使用。{dotted startColor="#ff6c6c" endColor="#1989fa"/}以下是进价 1、在变量中加入 ADD,本地优选域名/优选IP(支持多元素之间,或 换行 作间隔,如:168.1221.us.kg:443#优选 268.1221.us.kg:443#优选 ip.sb japan.com malaysia.com russia.com skk.moe www.visa.com www.visa.com.sg www.visa.com.hk www.visa.com.tw www.visakorea.com www.gov.se www.digitalocean.com www.whoer.net www.whatismyip.com www.hugedomains.com www.udacity.com www.4chan.org www.okcupid.com www.glassdoor.com www.udemy.com alejandracaiccedo.com log.bpminecraft.com www.boba88slot.com gur.gov.ua www.zsu.gov.ua www.iakeys.com www.d-555.com fbi.gov bestcf.onecf.eu.org#Mingyu大佬 cfip.xxxxxxxx.tk#OTC大佬 xn--b6gac.eu.org#优选域名2、在变量中加入 ADDAPI,如https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt https://wenben-738.pages.dev/ip.txt?token=9527 https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressesapi.txt3、在变量中加入 PROXYIP,如workers.cloudflare.cyou my-telegram-is-herocore.onecf.eu.org proxyip.fxxk.dedyn.io4、在变量中加入SUBAPI,clash、singbox等 订阅转换后端,如subapi.fxxk.dedyn.io5、在变量中加入ADDCSV,如https://raw.githubusercontent.com/cmliu/WorkerVless2sub/main/addressescsv.csv三、该项目部署的节点可通过节点PATH(路径)的方式,使用指定的PROXYIP或SOCKS5!!!1、指定 PROXYIP 案例 (仅限于域名开头为'proxyip.'的域名),可把jp改为其它国/proxyip=proxyip.jp.fxxk.dedyn.io /?proxyip=proxyip.jp.fxxk.dedyn.io /proxyip.jp.fxxk.dedyn.io2、指定 SOCKS5 案例/socks5=user:password@127.0.0.1:1080 /?socks5=user:password@127.0.0.1:1080 /socks://dXNlcjpwYXNzd29yZA==@127.0.0.1:1080 /socks5://user:password@127.0.0.1:1080等等
2024年07月18日
109 阅读
0 评论
0 点赞
2024-05-31
注册GCP150刀相关【有手就行】
注册GCP150刀相关【有手就行】地址: GCP 一路点下去就行,如果出现要验卡步骤就算了【或换个干净IP和环境再试】,换个号和窗口继续,手快有,手慢就要封车了。一、GCP 改成root密码登录方法1、先选择从浏览器打开ssh连接服务器2、切换到root账号,输入代码:sudo -i3、设置root密码,输入代码:passwd然后会要求输入新密码,然后再重复一次密码,输入密码的时候不会显示出来,所以直接输入密码,然后回车,再然后重复输入密码回车开启SSH权限1、CentOS和Debian通用,输入以下两条命令sed -i 's/PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config2、Ubuntu系统,输入以下两条命令sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config3、重启服务器,输入命令:reboot二、gcp亚洲速度较快各区台湾【tw】asia-east1 10.140.0.0/20香港【hk】asia-east2 10.170.0.0/20东京【tokyo】asia-northeast1 10.146.0.0/20大板【osaka】asia-northeast2 10.174.0.0/20首尔【seoul】asia-northeast3 10.178.0.0/20孟买asia-south1 10.160.0.0/20新德里asia-south2 10.190.0.0/20新加坡【sg】asia-southeast1 10.148.0.0/20加达asia-southeast2 10.184.0.0/20三、防火墙用:v4:0.0.0.0/0v6:::/0四、300刀创建免费200G注意事项1、地点必须是us-west(俗称美西)和另2种(不推荐)2、机型必须是e2-micro(2C 1G)3、磁盘选:标准和30G4、网络要换到:标准,us-west免费200G五、ip不好或被强时可更换ip从左上方选择 “VPC网络” 进入“ip地址”,操作方法看图
2024年05月31日
119 阅读
0 评论
0 点赞
1
2
您是第
25922
位访客