乐呵呵、欢的博客

lehhair's Blog

用cf works部署一个"阅后即焚毁"服务

2024-06-08

用cf works部署一个"阅后即焚毁"服务

直接上代码

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);
  if (request.method === 'POST' && url.pathname === '/create') {
    return await createDocument(request);
  } else if (request.method === 'GET' && url.pathname.startsWith('/doc/')) {
    return await getDocument(url.pathname.replace('/doc/', ''));
  } else if (request.method === 'POST' && url.pathname.startsWith('/delete/')) {
    return await deleteDocument(url.pathname.replace('/delete/', ''));
  } else {
    return new Response(renderHTML(), {
      headers: { 'Content-Type': 'text/html; charset=UTF-8' }
    });
  }
}

async function createDocument(request) {
  const { markdown, views, expiration } = await request.json();
  const viewsInt = views ? parseInt(views) : null;
  const expirationMs = expiration ? Date.now() + parseInt(expiration) * 60 * 1000 : null;

  if (views !== "" && (isNaN(viewsInt) || viewsInt < 0)) {
    return new Response(JSON.stringify({ error: '访问次数必须是非负整数' }), {
      status: 400,
      headers: { 'Content-Type': 'application/json; charset=UTF-8' }
    });
  }

  const id = generateId();
  const data = { markdown, views: viewsInt, expiration: expirationMs };
  await works_data.put(id, JSON.stringify(data));
  const link = `${new URL(request.url).origin}/doc/${id}`;
  return new Response(JSON.stringify({ link }), {
    headers: { 'Content-Type': 'application/json; charset=UTF-8' }
  });
}

async function getDocument(id) {
  const value = await works_data.get(id);
  if (!value) {
    const errorMessage = encodeBase64('不存在或已被焚毁。');
    return new Response(renderHTML(errorMessage, true, 0, true), {
      headers: { 'Content-Type': 'text/html; charset=UTF-8' }
    });
  }

  const data = JSON.parse(value);

  if (data.expiration && Date.now() > data.expiration) {
    await works_data.delete(id);
    const errorMessage = encodeBase64('文档已过期。');
    return new Response(renderHTML(errorMessage, true, 0, true), {
      headers: { 'Content-Type': 'text/html; charset=UTF-8' }
    });
  }

  if (data.views !== null) {
    data.views -= 1;
    if (data.views <= 0) {
      await works_data.delete(id);
    } else {
      await works_data.put(id, JSON.stringify(data));
    }
  }

  const encodedMarkdown = encodeBase64(data.markdown);
  const remainingTime = data.expiration ? Math.max(0, data.expiration - Date.now()) : null;
  return new Response(renderHTML(encodedMarkdown, true, data.views, false, remainingTime, id), {
    headers: { 'Content-Type': 'text/html; charset=UTF-8' }
  });
}

async function deleteDocument(id) {
  await works_data.delete(id);
  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json; charset=UTF-8' }
  });
}

function generateId() {
  return Math.random().toString(36).substr(2, 10);
}

function encodeBase64(str) {
  return btoa(String.fromCharCode(...new TextEncoder().encode(str)));
}

function decodeBase64(str) {
  return new TextDecoder().decode(new Uint8Array(atob(str).split('').map(char => char.charCodeAt(0))));
}

function formatRemainingTime(ms) {
  if (ms === null) return '不限时';
  const minutes = Math.floor(ms / 60000);
  const seconds = Math.floor((ms % 60000) / 1000);
  return `${minutes}${seconds}秒`;
}

function renderHTML(markdown = '', isDocPage = false, remainingViews = 0, isError = false, remainingTime = null, docId = '') {
  return `
  <!DOCTYPE html>
  <html lang="zh">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分享你的文档</title>
    <link rel="icon" type="image/png" href="data:image/png;base64,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">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/github-markdown.min.css">
    <link id="highlight-theme-light" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css">
    <link id="highlight-theme-dark" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled>
    <style>
    :root {
      --bg-color: #fff;
      --text-color: #24292e;
      --link-color: #0366d6;
      --border-color: #e1e4e8;
      --code-bg-color: #f6f8fa;
    }
    
    @media (prefers-color-scheme: dark) {
      :root {
        --bg-color: #0d1117;
        --text-color: #c9d1d9;
        --link-color: #58a6ff;
        --border-color: #30363d;
        --code-bg-color: #161b22;
      }
    }
    
    body {
      font-family: Arial, sans-serif;
      background-color: var(--bg-color);
      color: var(--text-color);
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      overflow: hidden;
      visibility: hidden;
    }
    
    .container {
      background-color: var(--bg-color);
      padding: 20px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
      border-radius: 8px;
      width: 80%;
      max-height: 80%;
      max-width: 80%;
      border: 1px solid var(--border-color);
      display: flex;
      flex-direction: column;
      resize: both; /* 允许容器调整大小 */
      overflow: auto; /* 当内容溢出时显示滚动条 */
    }
    
    @media (prefers-color-scheme: dark) {
      .container {
        box-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
      }
    }
    
    textarea, input {
      background-color: var(--bg-color);
      color: var(--text-color);
      border: 1px solid var(--border-color);
      width: 100%;
      margin-top: 10px;
      border-radius: 4px;
      padding: 10px;
      box-sizing: border-box;
      resize: none; /* 禁止内部元素调整大小 */
    }
    
    textarea {
      height: 200px;
      resize: vertical; /* 允许垂直方向调整大小 */
      min-height: 100px; /* 设置最小高度 */
    }
    
    button {
      background-color: var(--link-color);
      color: #fff;
      border: none;
      padding: 10px;
      cursor: pointer;
      border-radius: 4px;
      width: 100%;
      margin-top: 10px;
    }
    
    button:hover {
      opacity: 0.8;
    }
    
    #link {
      margin-top: 20px;
      cursor: pointer;
      color: var(--link-color);
      word-wrap: break-word; /* 允许长链接自动换行 */
    }
    
    #link:hover {
      text-decoration: underline;
    }
    
    .markdown-body {
      overflow-y: auto;
      color: var(--text-color);
      padding-right: 10px;
      -webkit-overflow-scrolling: touch;
    }
    
    .markdown-body pre {
      background-color: var(--code-bg-color);
      position: relative;
    }
    
    .markdown-body pre:hover .copy-btn {
      opacity: 1;
    }
    
    .copy-btn {
      position: absolute;
      top: 4px;
      right: 8px;
      width: 50px;
      height: 24px;
      background-color: var(--code-bg-color);
      border: 1px solid var(--border-color);
      color: var(--text-color);
      border-radius: 4px;
      cursor: pointer;
      opacity: 0;
      transition: opacity 0.3s;
      font-size: 12px;
      display: flex;
      justify-content: center;
      align-items: center;
      transform: translateY(-6px);
    }
    
    .theme-toggle {
      position: fixed;
      top: 10px;
      left: 10px;
      cursor: pointer;
      z-index: 1000;
    }
    
    .theme-toggle input {
      display: none;
    }
    
    .theme-toggle label {
      display: block;
      width: 40px;
      height: 20px;
      background-color: #ccc;
      border-radius: 20px;
      position: relative;
      transition: background-color 0.3s;
    }
    
    .theme-toggle label:before {
      content: "";
      display: block;
      width: 16px;
      height: 16px;
      border-radius: 50%;
      background-color: #fff;
      position: absolute;
      top: 2px;
      left: 2px;
      transition: transform 0.3s;
    }
    
    .theme-toggle input:checked + label {
      background-color: #2196F3;
    }
    
    .theme-toggle input:checked + label:before {
      transform: translateX(20px);
    }
    
    ::-webkit-scrollbar {
      width: 6px;
      height: 6px;
    }
    
    ::-webkit-scrollbar-track {
      background-color: var(--bg-color);
    }
    
    ::-webkit-scrollbar-thumb {
      background-color: var(--border-color);
      border-radius: 3px;
    }
    
    ::-webkit-scrollbar-thumb:hover {
      background-color: #aaa;
    }
    
    .notification {
      position: fixed;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      background-color: var(--link-color);
      color: #fff;
      padding: 10px 20px;
      border-radius: 4px;
      display: none;
      z-index: 1000;
    }
    
    .form-group {
      margin-bottom: 15px;
      flex: 1; /* 使表单组在容器中均匀分布 */
    }
    
    .form-group label {
      display: block;
      font-size: 14px;
      color: var(--text-color);
      margin-bottom: 5px;
    }
    
    .info-container {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-top: 20px;
      padding: 10px;
      border-radius: 4px;
      background-color: var(--code-bg-color);
    }
    
    .info-container p {
      margin: 0;
      font-size: 14px;
      color: var(--text-color);
    }
    
    .info-container button {
      width: auto;
      padding: 5px 10px;
      margin-left: 10px;
    }
    
    /* 调整按钮的大小 */
    .custom-button {
      width: 200px; /* 设置按钮的宽度 */
      height: 50px; /* 设置按钮的高度 */
      font-size: 18px; /* 设置按钮的字体大小 */
      padding: 10px 20px; /* 设置按钮的内边距 */
      background-color: var(--link-color); /* 设置按钮的背景颜色 */
      color: #fff; /* 设置按钮的文字颜色 */
      border: none; /* 移除按钮的边框 */
      border-radius: 4px; /* 设置按钮的圆角 */
      cursor: pointer; /* 设置鼠标悬停时的指针样式 */
      display: flex; /* 使用 flex 布局 */
      justify-content: center; /* 水平居中 */
      align-items: center; /* 垂直居中 */
      margin: 0; /* 重置默认的 margin */
    }
    
    .custom-button:hover {
      opacity: 0.8; /* 设置按钮悬停时的透明度 */
    }
    
    /* 使按钮在容器中垂直居中且靠右 */
    .button-wrapper {
      display: flex;
      justify-content: flex-end; /* 水平靠右 */
      align-items: center; /* 垂直居中 */
      width: 100%;
    }
    
    </style>
  </head>
  <body>
    <div class="theme-toggle">
      <input type="checkbox" id="theme-toggle-checkbox">
      <label for="theme-toggle-checkbox"></label>
    </div>
    <div class="container">
      ${isDocPage ? `
      <article class="markdown-body" id="markdown-container"></article>
      <div class="info-container">
        <div>
          <p>剩余可访问次数: ${remainingViews !== null ? remainingViews : '不限'}</p>
          <p>剩余时间: ${formatRemainingTime(remainingTime)}</p>
        </div>
        <button class="custom-button" onclick="confirmDestruction()">已读-确认销毁</button>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
      <script>
        const debounce = (func, wait) => {
          let timeout;
          return (...args) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
          };
        };

        const markdown = decodeBase64(${JSON.stringify(markdown)});
        marked.setOptions({
          gfm: true,
          breaks: true,
          headerIds: false,
          highlight: function(code, lang) {
            const language = hljs.getLanguage(lang) ? lang : 'plaintext';
            return hljs.highlight(code, { language }).value;
          }
        });
        const renderMarkdown = debounce(() => {
          document.getElementById('markdown-container').innerHTML = marked.parse(markdown);
          hljs.highlightAll();
          addCopyButtons();
        }, 300);

        const addCopyButtons = () => {
          const preElements = document.querySelectorAll('pre');
          preElements.forEach(pre => {
            const copyBtn = document.createElement('button');
            copyBtn.className = 'copy-btn';
            copyBtn.textContent = 'Copy';
            copyBtn.addEventListener('click', () => {
              const code = pre.querySelector('code').innerText;
              navigator.clipboard.writeText(code).then(() => {
                copyBtn.textContent = 'Copied!';
                setTimeout(() => {
                  copyBtn.textContent = 'Copy';
                }, 2000);
              });
            });
            pre.appendChild(copyBtn);
          });
        };

        const themeToggle = document.getElementById('theme-toggle-checkbox');
        const lightTheme = document.getElementById('highlight-theme-light');
        const darkTheme = document.getElementById('highlight-theme-dark');
        const setTheme = (isDark) => {
          document.documentElement.style.setProperty('--bg-color', isDark ? '#0d1117' : '#fff');
          document.documentElement.style.setProperty('--text-color', isDark ? '#c9d1d9' : '#24292e');
          document.documentElement.style.setProperty('--link-color', isDark ? '#58a6ff' : '#0366d6');
          document.documentElement.style.setProperty('--border-color', isDark ? '#30363d' : '#e1e4e8');
          document.documentElement.style.setProperty('--code-bg-color', isDark ? '#161b22' : '#f6f8fa');
          lightTheme.disabled = isDark;
          darkTheme.disabled = !isDark;
        };

        themeToggle.addEventListener('change', (event) => {
          setTheme(event.target.checked);
        });

        const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
        themeToggle.checked = prefersDark;
        setTheme(prefersDark);

        document.body.style.visibility = 'visible';
        renderMarkdown();

        function decodeBase64(str) {
          const decodedStr = atob(str);
          const data = new Uint8Array(decodedStr.split('').map(char => char.charCodeAt(0)));
          const decoder = new TextDecoder();
          return decoder.decode(data);
        }

        async function confirmDestruction() {
          const response = await fetch('/delete/${docId}', {
            method: 'POST'
          });
          const data = await response.json();
          if (data.success) {
            alert('文档已销毁');
            window.location.href = '/';
          } else {
            alert('销毁文档时出错');
          }
        }
      </script>
      ` : `
      <h1>分享一个秘密文档</h1>
      <div class="form-group">
        <label for="markdownText">输入你的 Markdown 内容</label>
        <textarea id="markdownText" placeholder="输入你的 Markdown 内容"></textarea>
      </div>
      <div class="form-group">
        <label for="views">输入可访问次数(留空表示不限次数)</label>
        <input type="number" id="views" value="5" min="0" step="1">
      </div>
      <div class="form-group">
        <label for="expiration">输入有效时间(单位分钟,留空表示不限时)</label>
        <input type="number" id="expiration" value="10" min="1" step="1">
      </div>
      <button onclick="createDocument()">生成分享链接</button>
      <p id="link" onclick="copyLink()"></p>
      <div class="notification" id="notification">链接已复制到剪贴板</div>
      <script>
        const debounce = (func, wait) => {
          let timeout;
          return (...args) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
          };
        };

        const createDocument = debounce(async () => {
          const markdown = document.getElementById('markdownText').value;
          const views = document.getElementById('views').value;
          const expiration = document.getElementById('expiration').value;
          const response = await fetch('/create', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ markdown, views, expiration })
          });
          const data = await response.json();
          if (data.error) {
            alert(data.error);
          } else {
            document.getElementById('link').textContent = data.link;
          }
        }, 300);

        const copyLink = debounce(() => {
          const link = document.getElementById('link').textContent;
          navigator.clipboard.writeText(link).then(() => {
            showNotification();
          });
        }, 300);

        const showNotification = () => {
          const notification = document.getElementById('notification');
          notification.style.display = 'block';
          setTimeout(() => {
            notification.style.display = 'none';
          }, 2000);
        };

        const themeToggle = document.getElementById('theme-toggle-checkbox');
        const lightTheme = document.getElementById('highlight-theme-light');
        const darkTheme = document.getElementById('highlight-theme-dark');
        const setTheme = (isDark) => {
          document.documentElement.style.setProperty('--bg-color', isDark ? '#0d1117' : '#fff');
          document.documentElement.style.setProperty('--text-color', isDark ? '#c9d1d9' : '#24292e');
          document.documentElement.style.setProperty('--link-color', isDark ? '#58a6ff' : '#0366d6');
          document.documentElement.style.setProperty('--border-color', isDark ? '#30363d' : '#e1e4e8');
          document.documentElement.style.setProperty('--code-bg-color', isDark ? '#161b22' : '#f6f8fa');
          lightTheme.disabled = isDark;
          darkTheme.disabled = !isDark;
        };

        themeToggle.addEventListener('change', (event) => {
          setTheme(event.target.checked);
        });

        const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
        themeToggle.checked = prefersDark;
        setTheme(prefersDark);

        document.body.style.visibility = 'visible';
      </script>`}
    </div>
  </body>
  </html>
  `;
}

将kv空间绑定变量works_data即可使用