查看: 535|回复: 0

[工具分享] Cloudflare Workers 记事本

[复制链接]
累计签到:349 天
连续签到:3 天

1114

主题

-15

回帖

1万

积分

热心会员

名望
126
星币
5031
星辰
9
好评
265
发表于 2024-9-4 14:56:35 | 显示全部楼层 |阅读模式

注册登录后全站资源免费查看下载

您需要 登录 才可以下载或查看,没有账号?立即注册

×
想记录一些安装命令文字啥的,找不到一个一劳永逸的开源項目,
还是 Cloudflare 大厂靠谱
需要登陆才能编辑,可以修改管理密码,黑夜模式.....
演示站(代码也在这里面了): https://loc.lol

部署说明:

1、创建 Cloudflare Workers (编辑代码将下面的代码复制~粘贴~部署)

2、创建 R2 存储桶(随意名称举例;AAA123)

3、在 Cloudflare Workers 设置~变量~R2 存储桶绑定~添加变量名称必须 JSBR2 选择R2存储桶 AAA123 部署即可。

注:修改了帖子里 树莓派 提出的问题手机浏览时,最底下的三两行代码会被状态栏遮住。

注:增加了帖子里 playbear 提出的问题 能不能输入密码后才能查看内容。

1、代码如下:(无需密码可查看内容)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


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

  4. async function handleRequest(request) {
  5.   const { pathname, searchParams } = new URL(request.url);

  6.   // Check if password protection is enabled
  7.   let ADMIN_PASSWORD = await JSBR2.get('admin_password');
  8.   ADMIN_PASSWORD = ADMIN_PASSWORD ? await ADMIN_PASSWORD.text() : null;

  9.   const isPasswordProtected = ADMIN_PASSWORD !== undefined && ADMIN_PASSWORD !== null;

  10.   console.log('isPasswordProtected:', isPasswordProtected);

  11.   if (request.method === 'GET' && pathname === '/') {
  12.     return new Response(await getHTML(isPasswordProtected), {
  13.       headers: { 'content-type': 'text/html;charset=UTF-8' },
  14.     });
  15.   }

  16.   if (request.method === 'GET' && pathname.startsWith('/notes/')) {
  17.     const key = pathname.replace('/notes/', '');
  18.     try {
  19.       const object = await JSBR2.get(key);
  20.       if (!object) {
  21.         return new Response('Note not found', {
  22.           headers: { 'content-type': 'text/plain;charset=UTF-8' },
  23.         });
  24.       }
  25.       const value = await object.text();
  26.       return new Response(value, {
  27.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  28.       });
  29.     } catch (err) {
  30.       console.error('Error retrieving note:', err);
  31.       return new Response('Error retrieving note', {
  32.         status: 500,
  33.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  34.       });
  35.     }
  36.   }

  37.   if (request.method === 'PUT' && pathname.startsWith('/notes/')) {
  38.     const key = pathname.replace('/notes/', '');
  39.     const password = searchParams.get('password');
  40.     if (isPasswordProtected && password !== ADMIN_PASSWORD) {
  41.       return new Response('Unauthorized', {
  42.         status: 401,
  43.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  44.       });
  45.     }
  46.     try {
  47.       const value = await request.text();
  48.       await JSBR2.put(key, value);
  49.       const timestamp = new Date().toISOString();
  50.       await JSBR2.put(`${key}_timestamp`, timestamp);
  51.       return new Response('Note saved', {
  52.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  53.       });
  54.     } catch (err) {
  55.       console.error('Error saving note:', err);
  56.       return new Response('Error saving note', {
  57.         status: 500,
  58.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  59.       });
  60.     }
  61.   }

  62.   if (request.method === 'POST' && pathname === '/set-password') {
  63.     const password = await request.text();
  64.     await JSBR2.put('admin_password', password);
  65.     return new Response('Password set', {
  66.       headers: { 'content-type': 'text/plain;charset=UTF-8' },
  67.     });
  68.   }

  69.   if (request.method === 'POST' && pathname === '/change-password') {
  70.     const { oldPassword, newPassword } = await request.json();
  71.     if (isPasswordProtected && oldPassword !== ADMIN_PASSWORD) {
  72.       return new Response('Unauthorized', {
  73.         status: 401,
  74.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  75.       });
  76.     }
  77.     await JSBR2.put('admin_password', newPassword);
  78.     return new Response('Password changed', {
  79.       headers: { 'content-type': 'text/plain;charset=UTF-8' },
  80.     });
  81.   }

  82.   return new Response('Not found', {
  83.     status: 404,
  84.     headers: { 'content-type': 'text/plain;charset=UTF-8' },
  85.   });
  86. }

  87. async function getHTML(isPasswordProtected) {
  88.   return `
  89.     <!DOCTYPE html>
  90.     <html>
  91.     <head>
  92.       <title>记事本</title>
  93.       <meta charset="UTF-8">
  94.       <meta name="viewport" content="width=device-width, initial-scale=1.0">
  95.       <style>
  96.         body, html {
  97.           margin: 0;
  98.           padding: 0;
  99.           height: 100%;
  100.           width: 100%;
  101.           display: flex;
  102.           flex-direction: column;
  103.           font-family: Arial, sans-serif;
  104.         }
  105.         textarea {
  106.           flex: 1;
  107.           width: 100%;
  108.           box-sizing: border-box;
  109.           padding: 10px;
  110.           font-size: 16px;
  111.           resize: none;
  112.         }
  113.         .status {
  114.           background-color: rgba(0, 0, 0, 0.5);
  115.           color: white;
  116.           padding: 10px;
  117.           border-radius: 5px;
  118.           font-size: 14px;
  119.           display: flex;
  120.           flex-direction: column;
  121.           align-items: flex-start;
  122.         }
  123.         .status-row {
  124.           display: flex;
  125.           justify-content: space-between;
  126.           width: 100%;
  127.         }
  128.         .status span, .status button {
  129.           margin: 5px 10px;
  130.         }
  131.         .password-setup,
  132.         .change-password {
  133.           display: none;
  134.           flex-direction: row;
  135.           align-items: center;
  136.           margin-top: 10px;
  137.         }
  138.         .password-setup input,
  139.         .change-password input {
  140.           margin-right: 10px;
  141.           margin-bottom: 5px;
  142.         }
  143.         .change-password input {
  144.           margin-right: 5px;
  145.         }
  146.         @media (max-width: 600px) {
  147.           .status {
  148.             position: static;
  149.             width: 100%;
  150.             padding: 5px;
  151.             font-size: 12px;
  152.           }
  153.           .status-row {
  154.             flex-direction: row;
  155.             flex-wrap: wrap;
  156.             justify-content: space-between;
  157.           }
  158.           .status span, .status button {
  159.             margin: 2px 5px;
  160.           }
  161.           .password-setup,
  162.           .change-password {
  163.             flex-direction: column;
  164.             align-items: flex-start;
  165.           }
  166.         }
  167.         @media (min-width: 601px) {
  168.           .status {
  169.             position: fixed;
  170.             bottom: 10px;
  171.             right: 10px;
  172.           }
  173.         }
  174.         .dark-mode {
  175.           background-color: black;
  176.           color: white;
  177.         }
  178.         .dark-mode textarea {
  179.           background-color: black;
  180.           color: white;
  181.         }
  182.         .dark-mode #moon-icon path {
  183.           fill: white;
  184.         }
  185.         #moon-icon path {
  186.           fill: black;
  187.         }
  188.       </style>
  189.     </head>
  190.     <body>
  191.       <textarea id="note" placeholder="开始输入..."></textarea>
  192.       <div class="status" id="status">
  193.         <div class="status-row">
  194.           <span>当前已使用:<span id="used-space">0.00 KB</span></span>
  195.           <span>总剩余空间:<span id="remaining-space">9.99 GB</span></span>
  196.         </div>
  197.         <div class="status-row">
  198.           <span id="last-write-time-container">最/后写入时间:<span id="last-write-time">N/A</span></span>
  199.           <button id="change-password-button" onclick="showChangePassword()" style="display: none; margin-right: 20px;">修改密码</button>
  200.         </div>
  201.         <div class="password-setup" id="password-setup">
  202.           <input type="password" id="admin-password" placeholder="设置管理员密码" />
  203.           <button onclick="setPassword()">确认</button>
  204.         </div>
  205.         <div class="change-password" id="change-password">
  206.           <input type="password" id="old-password" placeholder="旧密码" />
  207.           <input type="password" id="new-password" placeholder="新密码" />
  208.           <button onclick="changePassword()">确定</button>
  209.         </div>
  210.       </div>
  211.       <svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" style="position: fixed; top: 10px; right: 10px; cursor: pointer;">
  212.         <path d="M12 2a10 10 0 0 0 0 20 10 10 0 0 0 0-20zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16z"/>
  213.       </svg>
  214.       <script>
  215.         const TOTAL_STORAGE = 10 * 1024 * 1024 * 1024; // 10 GB in bytes

  216.         function updateStorageStatus() {
  217.           const note = document.getElementById('note').value;
  218.           const usedBytes = new TextEncoder().encode(note).length;
  219.           const remainingBytes = TOTAL_STORAGE - usedBytes;

  220.           document.getElementById('used-space').textContent = formatBytes(usedBytes);
  221.           document.getElementById('remaining-space').textContent = formatBytes(remainingBytes);
  222.         }

  223.         function formatBytes(bytes) {
  224.           const units = ['B', 'KB', 'MB', 'GB'];
  225.           let unitIndex = 0;
  226.           let value = bytes;

  227.           while (value >= 1024 && unitIndex < units.length - 1) {
  228.             value /= 1024;
  229.             unitIndex++;
  230.           }

  231.           return value.toFixed(2) + ' ' + units[unitIndex];
  232.         }

  233.         async function saveNote() {
  234.           const note = document.getElementById('note').value;
  235.           let password = localStorage.getItem('adminPassword');
  236.           if (${isPasswordProtected} && !password) {
  237.             password = prompt('请输入管理员密码:');
  238.             if (password) {
  239.               localStorage.setItem('adminPassword', password);
  240.               document.getElementById('change-password-button').style.display = 'inline-block';
  241.             } else {
  242.               alert('需要密码才能编辑笔记。');
  243.               return;
  244.             }
  245.           }
  246.           const response = await fetch(\`/notes/my-note?password=\${password || ''}\`, {
  247.             method: 'PUT',
  248.             body: note,
  249.             headers: {
  250.               'Content-Type': 'text/plain;charset=UTF-8'
  251.             }
  252.           });
  253.           if (response.status === 401) {
  254.             alert('密码错误。请刷新页面并输入正确的密码。');
  255.             localStorage.removeItem('adminPassword');
  256.             document.getElementById('change-password-button').style.display = 'none';
  257.             return;
  258.           }
  259.           const timestamp = new Date().toISOString();
  260.           document.getElementById('last-write-time').textContent = new Date(timestamp).toLocaleString();
  261.           document.getElementById('last-write-time-container').style.display = 'inline'; // 显示最/后写入时间
  262.         }

  263.         async function loadNote() {
  264.           const response = await fetch('/notes/my-note');
  265.           const note = await response.text();
  266.           document.getElementById('note').value = note;
  267.           updateStorageStatus();
  268.           updateLastWriteTime(); // Initial load of last write time
  269.         }

  270.         async function updateLastWriteTime() {
  271.           const timestampResponse = await fetch('/notes/my-note_timestamp');
  272.           if (timestampResponse.ok) {
  273.             const timestamp = await timestampResponse.text();
  274.             const localTimestamp = new Date(timestamp).toLocaleString();
  275.             document.getElementById('last-write-time').textContent = localTimestamp;
  276.             document.getElementById('last-write-time-container').style.display = 'inline'; // 显示最/后写入时间
  277.           } else {
  278.             document.getElementById('last-write-time-container').style.display = 'none'; // 隐藏最/后写入时间
  279.           }
  280.         }

  281.         function debounce(func, wait) {
  282.           let timeout;
  283.           return function() {
  284.             const context = this, args = arguments;
  285.             clearTimeout(timeout);
  286.             timeout = setTimeout(() => func.apply(context, args), wait);
  287.           };
  288.         }

  289.         const debouncedSaveNote = debounce(saveNote, 200);

  290.         document.getElementById('note').addEventListener('input', () => {
  291.           debouncedSaveNote();
  292.           updateStorageStatus();
  293.         });

  294.         window.addEventListener('load', () => {
  295.           loadNote();
  296.           setInterval(updateLastWriteTime, 1000); // 每秒更新一次最/后写入时间

  297.           const password = localStorage.getItem('adminPassword');
  298.           if (password) {
  299.             document.getElementById('change-password-button').style.display = 'inline-block';
  300.           }
  301.         });

  302.         async function setPassword() {
  303.           const password = document.getElementById('admin-password').value;
  304.           if (password) {
  305.             const response = await fetch('/set-password', {
  306.               method: 'POST',
  307.               body: password,
  308.               headers: {
  309.                 'Content-Type': 'text/plain;charset=UTF-8'
  310.               }
  311.             });
  312.             if (response.ok) {
  313.               alert('管理员密码设置成功');
  314.               document.getElementById('password-setup').style.display = 'none';
  315.             } else {
  316.               alert('设置管理员密码失败');
  317.             }
  318.           } else {
  319.             alert('请输入密码');
  320.           }
  321.         }

  322.         function showChangePassword() {
  323.           const password = localStorage.getItem('adminPassword');
  324.           if (!password) {
  325.             alert('您尚未登陆,请登陆后再修改密码');
  326.             return;
  327.           }
  328.           document.getElementById('change-password').style.display = 'flex';
  329.           document.getElementById('change-password-button').style.display = 'none';
  330.         }

  331.         async function changePassword() {
  332.           const oldPassword = document.getElementById('old-password').value;
  333.           const newPassword = document.getElementById('new-password').value;
  334.           if (!oldPassword || !newPassword) {
  335.             alert('请输入旧密码和新密码');
  336.             return;
  337.           }
  338.           const response = await fetch('/change-password', {
  339.             method: 'POST',
  340.             body: JSON.stringify({ oldPassword, newPassword }),
  341.             headers: {
  342.               'Content-Type': 'application/json;charset=UTF-8'
  343.             }
  344.           });
  345.           if (response.status === 401) {
  346.             alert('旧密码不正确');
  347.             return;
  348.           }
  349.           if (response.ok) {
  350.             alert('密码修改成功');
  351.             document.getElementById('change-password').style.display = 'none';
  352.             localStorage.setItem('adminPassword', newPassword);
  353.           } else {
  354.             alert('密码修改失败');
  355.           }
  356.         }

  357.         document.getElementById('moon-icon').addEventListener('click', () => {
  358.           document.body.classList.toggle('dark-mode');
  359.         });
  360.       </script>
  361.     </body>
  362.     </html>
  363.   `;
  364. }






















  365. 2、代码如下:(需要密码可查看内容)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~












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

  369. async function handleRequest(request) {
  370.   const { pathname, searchParams } = new URL(request.url);

  371.   // Check if password protection is enabled
  372.   let ADMIN_PASSWORD = await JSBR2.get('admin_password');
  373.   ADMIN_PASSWORD = ADMIN_PASSWORD ? await ADMIN_PASSWORD.text() : null;

  374.   const isPasswordProtected = ADMIN_PASSWORD !== undefined && ADMIN_PASSWORD !== null;

  375.   console.log('isPasswordProtected:', isPasswordProtected);

  376.   if (request.method === 'GET' && pathname === '/') {
  377.     return new Response(await getHTML(isPasswordProtected), {
  378.       headers: { 'content-type': 'text/html;charset=UTF-8' },
  379.     });
  380.   }

  381.   if (request.method === 'GET' && pathname.startsWith('/notes/')) {
  382.     const key = pathname.replace('/notes/', '');
  383.     const password = searchParams.get('password');

  384.     if (isPasswordProtected && password !== ADMIN_PASSWORD) {
  385.       return new Response('Unauthorized', {
  386.         status: 401,
  387.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  388.       });
  389.     }

  390.     try {
  391.       const object = await JSBR2.get(key);
  392.       if (!object) {
  393.         return new Response('Note not found', {
  394.           headers: { 'content-type': 'text/plain;charset=UTF-8' },
  395.         });
  396.       }
  397.       const value = await object.text();
  398.       return new Response(value, {
  399.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  400.       });
  401.     } catch (err) {
  402.       console.error('Error retrieving note:', err);
  403.       return new Response('Error retrieving note', {
  404.         status: 500,
  405.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  406.       });
  407.     }
  408.   }

  409.   if (request.method === 'PUT' && pathname.startsWith('/notes/')) {
  410.     const key = pathname.replace('/notes/', '');
  411.     const password = searchParams.get('password');
  412.     if (isPasswordProtected && password !== ADMIN_PASSWORD) {
  413.       return new Response('Unauthorized', {
  414.         status: 401,
  415.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  416.       });
  417.     }
  418.     try {
  419.       const value = await request.text();
  420.       await JSBR2.put(key, value);
  421.       const timestamp = new Date().toISOString();
  422.       await JSBR2.put(`${key}_timestamp`, timestamp);
  423.       return new Response('Note saved', {
  424.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  425.       });
  426.     } catch (err) {
  427.       console.error('Error saving note:', err);
  428.       return new Response('Error saving note', {
  429.         status: 500,
  430.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  431.       });
  432.     }
  433.   }

  434.   if (request.method === 'POST' && pathname === '/set-password') {
  435.     const password = await request.text();
  436.     await JSBR2.put('admin_password', password);
  437.     return new Response('Password set', {
  438.       headers: { 'content-type': 'text/plain;charset=UTF-8' },
  439.     });
  440.   }

  441.   if (request.method === 'POST' && pathname === '/change-password') {
  442.     const { oldPassword, newPassword } = await request.json();
  443.     if (isPasswordProtected && oldPassword !== ADMIN_PASSWORD) {
  444.       return new Response('Unauthorized', {
  445.         status: 401,
  446.         headers: { 'content-type': 'text/plain;charset=UTF-8' },
  447.       });
  448.     }
  449.     await JSBR2.put('admin_password', newPassword);
  450.     return new Response('Password changed', {
  451.       headers: { 'content-type': 'text/plain;charset=UTF-8' },
  452.     });
  453.   }

  454.   return new Response('Not found', {
  455.     status: 404,
  456.     headers: { 'content-type': 'text/plain;charset=UTF-8' },
  457.   });
  458. }

  459. async function getHTML(isPasswordProtected) {
  460.   return `
  461.     <!DOCTYPE html>
  462.     <html>
  463.     <head>
  464.       <title>记事本</title>
  465.       <meta charset="UTF-8">
  466.       <meta name="viewport" content="width=device-width, initial-scale=1.0">
  467.       <style>
  468.         body, html {
  469.           margin: 0;
  470.           padding: 0;
  471.           height: 100%;
  472.           width: 100%;
  473.           display: flex;
  474.           flex-direction: column;
  475.           font-family: Arial, sans-serif;
  476.         }
  477.         textarea {
  478.           flex: 1;
  479.           width: 100%;
  480.           box-sizing: border-box;
  481.           padding: 10px;
  482.           font-size: 16px;
  483.           resize: none;
  484.         }
  485.         .status {
  486.           background-color: rgba(0, 0, 0, 0.5);
  487.           color: white;
  488.           padding: 10px;
  489.           border-radius: 5px;
  490.           font-size: 14px;
  491.           display: flex;
  492.           flex-direction: column;
  493.           align-items: flex-start;
  494.         }
  495.         .status-row {
  496.           display: flex;
  497.           justify-content: space-between;
  498.           width: 100%;
  499.         }
  500.         .status span, .status button {
  501.           margin: 5px 10px;
  502.         }
  503.         .password-setup,
  504.         .change-password {
  505.           display: none;
  506.           flex-direction: row;
  507.           align-items: center;
  508.           margin-top: 10px;
  509.         }
  510.         .password-setup input,
  511.         .change-password input {
  512.           margin-right: 10px;
  513.           margin-bottom: 5px;
  514.         }
  515.         .change-password input {
  516.           margin-right: 5px;
  517.         }
  518.         @media (max-width: 600px) {
  519.           .status {
  520.             position: static;
  521.             width: 100%;
  522.             padding: 5px;
  523.             font-size: 12px;
  524.           }
  525.           .status-row {
  526.             flex-direction: row;
  527.             flex-wrap: wrap;
  528.             justify-content: space-between;
  529.           }
  530.           .status span, .status button {
  531.             margin: 2px 5px;
  532.           }
  533.           .password-setup,
  534.           .change-password {
  535.             flex-direction: column;
  536.             align-items: flex-start;
  537.           }
  538.         }
  539.         @media (min-width: 601px) {
  540.           .status {
  541.             position: fixed;
  542.             bottom: 10px;
  543.             right: 10px;
  544.           }
  545.         }
  546.         .dark-mode {
  547.           background-color: black;
  548.           color: white;
  549.         }
  550.         .dark-mode textarea {
  551.           background-color: black;
  552.           color: white;
  553.         }
  554.         .dark-mode #moon-icon path {
  555.           fill: white;
  556.         }
  557.         #moon-icon path {
  558.           fill: black;
  559.         }
  560.       </style>
  561.     </head>
  562.     <body>
  563.       <textarea id="note" placeholder="请输入密码以查看内容" disabled></textarea>
  564.       <div class="status" id="status">
  565.         <div class="status-row">
  566.           <span>当前已使用:<span id="used-space">0.00 KB</span></span>
  567.           <span>总剩余空间:<span id="remaining-space">9.99 GB</span></span>
  568.         </div>
  569.         <div class="status-row">
  570.           <span id="last-write-time-container">最/后写入时间:<span id="last-write-time">N/A</span></span>
  571.           <button id="change-password-button" onclick="showChangePassword()" style="display: none; margin-right: 20px;">修改密码</button>
  572.         </div>
  573.         <div class="password-setup" id="password-setup">
  574.           <input type="password" id="admin-password" placeholder="设置管理员密码" />
  575.           <button onclick="setPassword()">确认</button>
  576.         </div>
  577.         <div class="change-password" id="change-password">
  578.           <input type="password" id="old-password" placeholder="旧密码" />
  579.           <input type="password" id="new-password" placeholder="新密码" />
  580.           <button onclick="changePassword()">确定</button>
  581.         </div>
  582.       </div>
  583.       <svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" style="position: fixed; top: 10px; right: 10px; cursor: pointer;">
  584.         <path d="M12 2a10 10 0 0 0 0 20 10 10 0 0 0 0-20zm0 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16z"/>
  585.       </svg>
  586.       <script>
  587.         const TOTAL_STORAGE = 10 * 1024 * 1024 * 1024; // 10 GB in bytes

  588.         function updateStorageStatus() {
  589.           const note = document.getElementById('note').value;
  590.           const usedBytes = new TextEncoder().encode(note).length;
  591.           const remainingBytes = TOTAL_STORAGE - usedBytes;

  592.           document.getElementById('used-space').textContent = formatBytes(usedBytes);
  593.           document.getElementById('remaining-space').textContent = formatBytes(remainingBytes);
  594.         }

  595.         function formatBytes(bytes) {
  596.           const units = ['B', 'KB', 'MB', 'GB'];
  597.           let unitIndex = 0;
  598.           let value = bytes;

  599.           while (value >= 1024 && unitIndex < units.length - 1) {
  600.             value /= 1024;
  601.             unitIndex++;
  602.           }

  603.           return value.toFixed(2) + ' ' + units[unitIndex];
  604.         }

  605.         async function saveNote() {
  606.           const note = document.getElementById('note').value;
  607.           let password = localStorage.getItem('adminPassword');
  608.           if (${isPasswordProtected} && !password) {
  609.             password = prompt('请输入管理员密码:');
  610.             if (password) {
  611.               localStorage.setItem('adminPassword', password);
  612.               document.getElementById('change-password-button').style.display = 'inline-block';
  613.             } else {
  614.               alert('需要密码才能编辑笔记。');
  615.               return;
  616.             }
  617.           }
  618.           const response = await fetch(\`/notes/my-note?password=\${password || ''}\`, {
  619.             method: 'PUT',
  620.             body: note,
  621.             headers: {
  622.               'Content-Type': 'text/plain;charset=UTF-8'
  623.             }
  624.           });
  625.           if (response.status === 401) {
  626.             alert('密码错误。请刷新页面并输入正确的密码。');
  627.             localStorage.removeItem('adminPassword');
  628.             document.getElementById('change-password-button').style.display = 'none';
  629.             return;
  630.           }
  631.           const timestamp = new Date().toISOString();
  632.           document.getElementById('last-write-time').textContent = new Date(timestamp).toLocaleString();
  633.           document.getElementById('last-write-time-container').style.display = 'inline'; // 显示最/后写入时间
  634.         }

  635.         async function loadNote() {
  636.           let password = localStorage.getItem('adminPassword');
  637.           if (${isPasswordProtected} && !password) {
  638.             password = prompt('请输入管理员密码:');
  639.             if (password) {
  640.               localStorage.setItem('adminPassword', password);
  641.             } else {
  642.               alert('需要密码才能查看笔记内容。');
  643.               return;
  644.             }
  645.           }
  646.           const response = await fetch(\`/notes/my-note?password=\${password || ''}\`);
  647.           if (response.status === 401) {
  648.             alert('密码错误。请刷新页面并输入正确的密码。');
  649.             localStorage.removeItem('adminPassword');
  650.             return;
  651.           }
  652.           const note = await response.text();
  653.           document.getElementById('note').value = note;
  654.           document.getElementById('note').disabled = false; // 启用输入框
  655.           updateStorageStatus();
  656.           updateLastWriteTime(); // Initial load of last write time
  657.         }

  658.         async function updateLastWriteTime() {
  659.           const timestampResponse = await fetch('/notes/my-note_timestamp');
  660.           if (timestampResponse.ok) {
  661.             const timestamp = await timestampResponse.text();
  662.             const localTimestamp = new Date(timestamp).toLocaleString();
  663.             document.getElementById('last-write-time').textContent = localTimestamp;
  664.             document.getElementById('last-write-time-container').style.display = 'inline'; // 显示最/后写入时间
  665.           } else {
  666.             document.getElementById('last-write-time-container').style.display = 'none'; // 隐藏最/后写入时间
  667.           }
  668.         }

  669.         function debounce(func, wait) {
  670.           let timeout;
  671.           return function() {
  672.             const context = this, args = arguments;
  673.             clearTimeout(timeout);
  674.             timeout = setTimeout(() => func.apply(context, args), wait);
  675.           };
  676.         }

  677.         const debouncedSaveNote = debounce(saveNote, 200);

  678.         document.getElementById('note').addEventListener('input', () => {
  679.           debouncedSaveNote();
  680.           updateStorageStatus();
  681.         });

  682.         window.addEventListener('load', () => {
  683.           loadNote();
  684.           setInterval(updateLastWriteTime, 1000); // 每秒更新一次最/后写入时间

  685.           const password = localStorage.getItem('adminPassword');
  686.           if (password) {
  687.             document.getElementById('change-password-button').style.display = 'inline-block';
  688.           }
  689.         });

  690.         async function setPassword() {
  691.           const password = document.getElementById('admin-password').value;
  692.           if (password) {
  693.             const response = await fetch('/set-password', {
  694.               method: 'POST',
  695.               body: password,
  696.               headers: {
  697.                 'Content-Type': 'text/plain;charset=UTF-8'
  698.               }
  699.             });
  700.             if (response.ok) {
  701.               alert('管理员密码设置成功');
  702.               document.getElementById('password-setup').style.display = 'none';
  703.             } else {
  704.               alert('设置管理员密码失败');
  705.             }
  706.           } else {
  707.             alert('请输入密码');
  708.           }
  709.         }

  710.         function showChangePassword() {
  711.           const password = localStorage.getItem('adminPassword');
  712.           if (!password) {
  713.             alert('您尚未登陆,请登陆后再修改密码');
  714.             return;
  715.           }
  716.           document.getElementById('change-password').style.display = 'flex';
  717.           document.getElementById('change-password-button').style.display = 'none';
  718.         }

  719.         async function changePassword() {
  720.           const oldPassword = document.getElementById('old-password').value;
  721.           const newPassword = document.getElementById('new-password').value;
  722.           if (!oldPassword || !newPassword) {
  723.             alert('请输入旧密码和新密码');
  724.             return;
  725.           }
  726.           const response = await fetch('/change-password', {
  727.             method: 'POST',
  728.             body: JSON.stringify({ oldPassword, newPassword }),
  729.             headers: {
  730.               'Content-Type': 'application/json;charset=UTF-8'
  731.             }
  732.           });
  733.           if (response.status === 401) {
  734.             alert('旧密码不正确');
  735.             return;
  736.           }
  737.           if (response.ok) {
  738.             alert('密码修改成功');
  739.             document.getElementById('change-password').style.display = 'none';
  740.             localStorage.setItem('adminPassword', newPassword);
  741.           } else {
  742.             alert('密码修改失败');
  743.           }
  744.         }

  745.         document.getElementById('moon-icon').addEventListener('click', () => {
  746.           document.body.classList.toggle('dark-mode');
  747.         });
  748.       </script>
  749.     </body>
  750.     </html>
  751.   `;
  752. }
复制代码



默认签名:偏爱是我家,发展靠大家! 社区反馈邮箱Mail To:service@pai.al或paijishu@outlook.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|偏爱技术社区-偏爱技术吧-源码-科学刀-我爱辅助-娱乐网--教开服-游戏源码

偏爱技术社区-偏爱技术吧-源码-科学刀-我爱辅助-娱乐网-游戏源码

Powered by Discuz! X3.5

GMT+8, 2024-12-26 01:58 , Processed in 0.083149 second(s), 33 queries .

快速回复 返回顶部 返回列表