xChar
·2 months ago

WARNING:滥用代码仓库(如Github/GitLab)可能有封号风险!

基本原理

Pages通过Git存储库部署,Workers通过API Key访问你的Github仓库目录开放查询API,Pages读取这个API,展示文件

实例

image

创建Github仓库

  1. 不教了,简单的雅痞。网上的教程比我的头发还多
  2. 根目录创建file文件夹,后续的文件上传下载都在这个文件夹
  3. 根目录创建index.html,填写代码:
    更改Body的查询API URL为你的:
    const response = await fetch('https://file-up.afo.im/list');
    你目前不知道要改成什么,先往下看
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>File List</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 20px;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      margin: 5px 0;
    }
    a {
      text-decoration: none;
      color: #007bff;
    }
    a:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <h1>File List</h1>
  <ul id="file-list"></ul>

  <script>
    async function fetchFileList() {
      try {
        const response = await fetch('https://file-up.afo.im/list');
        if (!response.ok) {
          throw new Error('Failed to fetch file list');
        }
        const fileList = await response.text();
        const files = fileList.split('\n').filter(file => file.trim() !== '');
        displayFiles(files);
      } catch (error) {
        console.error('Error fetching file list:', error);
        document.getElementById('file-list').innerHTML = '<li>Error fetching file list</li>';
      }
    }

    function displayFiles(files) {
      const fileListElement = document.getElementById('file-list');
      fileListElement.innerHTML = files.map(file => 
        `<li><a href="/file/${encodeURIComponent(file)}">${file}</a></li>`
      ).join('');
    }

    fetchFileList();
  </script>
</body>
</html>

创建Workers,开放API查询和上传端点

  1. 不教了,简单的雅痞。可见创建 Workers,连接 R2不用看连接R2的部分
  2. 更改Workers代码:
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const url = new URL(request.url)
  const path = url.pathname

  if (path === '/') {
    return addCORSHeaders(showLoginForm())
  } else if (path === '/login') {
    return addCORSHeaders(await handleLogin(request))
  } else if (path === '/upload') {
    return addCORSHeaders(await handleUpload(request))
  } else if (path === '/check-config') {
    return addCORSHeaders(await checkConfig())
  } else if (path === '/list') {
    return addCORSHeaders(await listFiles()) // 允许直接访问 /list,无需授权
  } else {
    return addCORSHeaders(new Response('Not Found', { status: 404 }))
  }
}

function showLoginForm() {
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Login</title>
    </head>
    <body>
      <h1>Login</h1>
      <form action="/login" method="POST">
        <input type="password" name="password" placeholder="Enter password" required>
        <button type="submit">Login</button>
      </form>
      <p><a href="/check-config">Check server configuration</a></p>
    </body>
    </html>
  `
  return new Response(html, {
    headers: { 'Content-Type': 'text/html' }
  })
}

async function handleLogin(request) {
  const formData = await request.formData()
  const password = formData.get('password')

  if (password === PASSWORD) {
    return showUploadForm()
  } else {
    return new Response('Invalid password', { status: 401 })
  }
}

function showUploadForm() {
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>File Upload</title>
    </head>
    <body>
      <h1>File Upload</h1>
      <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" required>
        <button type="submit">Upload</button>
      </form>
    </body>
    </html>
  `
  return new Response(html, {
    headers: { 'Content-Type': 'text/html' }
  })
}

async function handleUpload(request) {
  try {
    const formData = await request.formData()
    const file = formData.get('file')

    if (!file) {
      return new Response('No file uploaded', { status: 400 })
    }

    const content = await file.arrayBuffer()
    const encodedContent = btoa(String.fromCharCode.apply(null, new Uint8Array(content)))

    const githubResponse = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/contents/file/${file.name}`, {
      method: 'PUT',
      headers: {
        'Authorization': `token ${GITHUB_TOKEN}`,
        'Content-Type': 'application/json',
        'User-Agent': 'Cloudflare Worker'
      },
      body: JSON.stringify({
        message: `Upload ${file.name}`,
        content: encodedContent
      })
    })

    if (githubResponse.ok) {
      return new Response('File uploaded successfully', { status: 200 })
    } else {
      const errorData = await githubResponse.text()
      console.error('GitHub API Error:', errorData)
      return new Response(`Failed to upload file: ${errorData}`, { status: 500 })
    }
  } catch (error) {
    console.error('Upload error:', error)
    return new Response(`Error during upload: ${error.message}`, { status: 500 })
  }
}

async function listFiles() {
  try {
    const githubResponse = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/contents/file`, {
      headers: {
        'Authorization': `token ${GITHUB_TOKEN}`,
        'User-Agent': 'Cloudflare Worker'
      }
    })

    if (!githubResponse.ok) {
      const errorData = await githubResponse.text()
      console.error('GitHub API Error:', errorData)
      return new Response(`Failed to fetch file list: ${errorData}`, { status: 500 })
    }

    const files = await githubResponse.json()

    // 将文件名转换为纯文本,一行一个
    const fileNames = files.map(file => file.name).join('\n')

    return new Response(fileNames, {
      headers: { 'Content-Type': 'text/plain' }
    })
  } catch (error) {
    console.error('Error fetching files:', error)
    return new Response(`Error during fetching files: ${error.message}`, { status: 500 })
  }
}

async function checkConfig() {
  let configStatus = 'All configurations are set correctly.'

  if (!PASSWORD) {
    configStatus = 'ERROR: PASSWORD is not set.'
  } else if (!GITHUB_TOKEN) {
    configStatus = 'ERROR: GITHUB_TOKEN is not set.'
  } else if (!GITHUB_REPO) {
    configStatus = 'ERROR: GITHUB_REPO is not set.'
  } else {
    try {
      const response = await fetch(`https://api.github.com/repos/${GITHUB_REPO}`, {
        headers: {
          'Authorization': `token ${GITHUB_TOKEN}`,
          'User-Agent': 'Cloudflare Worker'
        }
      })

      const responseText = await response.text()

      if (!response.ok) {
        try {
          const data = JSON.parse(responseText)
          configStatus = `ERROR: GitHub API returned an error: ${data.message}`
        } catch (jsonError) {
          configStatus = `ERROR: GitHub API returned a non-JSON response. Status: ${response.status}, Body: ${responseText.substring(0, 100)}...`
        }
      } else {
        try {
          JSON.parse(responseText)
          configStatus += ' GitHub connection successful.'
        } catch (jsonError) {
          configStatus = `WARNING: GitHub API returned a non-JSON response, but the connection was successful. Status: ${response.status}, Body: ${responseText.substring(0, 100)}...`
        }
      }
    } catch (error) {
      configStatus = `ERROR: Failed to connect to GitHub API: ${error.message}`
    }
  }

  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Configuration Check</title>
    </head>
    <body>
      <h1>Configuration Check</h1>
      <p>${configStatus}</p>
      <a href="/">Back to Login</a>
    </body>
    </html>
  `

  return new Response(html, {
    status: 200,
    headers: { 'Content-Type': 'text/html' }
  })
}

// 添加 CORS 头
function addCORSHeaders(response) {
  const headers = new Headers(response.headers)
  headers.set('Access-Control-Allow-Origin', '*')
  headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS')
  headers.set('Access-Control-Allow-Headers', 'Content-Type')

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers
  })
}
  1. 为Workers添加变量
    GITHUB_REPO格式为:Github用户名/Github仓库名
    GITHUB_TOKEN格式为:你的Github API Key。需要有仓库(Repository)修改权限
    PASSWORD为访问上传端点的密码,自行设置(更建议使用Git上传)
    image

  2. 访问你的自定义域的/list,看看查询API是否正常,是否能列出仓库下file文件夹下的文件,如

image

  1. 将你的URL填入创建Github仓库的第三步中的

const response = await fetch('https://file-up.afo.im/list');
替换https://file-up.afo.im/list为你的

创建Pages,连接Git存储库

  1. 不教了,简单的雅痞。可见教你搭建你的第一个网站!无需服务器即可上手!注意要用Git存储库部署而不是本地部署

  2. Cloudflare Pages当有提交时会自动重新部署,实现新文件自动更新

访问

image

  • 写的仓促,有问题评论问
Loading comments...