利用cloudflare works搭建个人网盘站——免费免服务器搭建基于onedrive的网盘站(2020版)

 

FODI是一款 OneDrive 秒级列表程序,之前就支持腾讯云SFC搭建基于onedrive的网盘站,但是现在腾讯云api要收费了(cloudflare的访问速度没有腾讯云的SFC快),所以其作者增加了cloudflare workers版。其好处是:免费、不需要服务器。现在这个FODI并不是仅需cloudflare workers就能搭建的,其前端还需使用github pages搭建,那样挺麻烦的。我就修改了几行代码,使得其前端能在cloudflare上部署,以达到部署简单的效果,不会出现奇奇怪怪的bug。

FODI项目作者github地址(此项目支持cloudflare和腾讯云函数SFC搭建):

https://github.com/vcheckzen/FODI

我的演示地址:

https://cf-onedrive.elle.workers.dev/

 为项目添加域名可参考

https://blog.csdn.net/qq_38243612/article/details/104433673

 1、国际版点击获取onedrive的refresh_token,点击Get a refresh token

https://service-36wivxsc-1256127833.ap-hongkong.apigateway.myqcloud.com/release/scf_onedrive_filelistor

2、登录office账号

 3、点击接受

 4、保存refresh_token内容

5、准备好cloudflare账号,登录以下地址https://workers.cloudflare.com/,登录后跳转到下图位置,点击workers

 6、点击create a worker,第一次使用会让你添加一个后缀,设置后无法修改(格式x.workers.dev)

 7、清除下图红色位置代码,粘贴此项目后端代码(下一步骤有代码)。代码地址

https://github.com/vcheckzen/FODI/blob/master/back-end-cf/index.js

 8、将ONEDRIVE_REFRESH_TOKEN 修改为之前保存的refresh_token值,注意在对应的双引号之间,别删了。想要显示特定的目录可填写EXPOSE_PATH 变量,如:/测试文件,想要显示所有,留空即可(如果是世纪互联,还需将IS_CN改为1),保存,记录项目地址。

const IS_CN = 0;
const EXPOSE_PATH = "路径"
const ONEDRIVE_REFRESHTOKEN = "refresh_token"


async function handleRequest(request) {
  let requestPath
  let querySplited
  let queryString = request.url.split('?')[1]
  if (queryString) {
    querySplited = queryString.split('=')
  }
  if (querySplited && querySplited[0] === 'file') {
    const file = querySplited[1]
    const fileName = file.split('/').pop();
    requestPath = file.replace('/' + fileName, '')
    const url = await fetchFiles(requestPath, fileName)
    return Response.redirect(url, 302)
  } else {
    const { headers } = request
    const contentType = headers.get('content-type')
    let body={}
    if (contentType && contentType.includes('form')) {
      const formData = await request.formData()
      for (let entry of formData.entries()) {
        body[entry[0]] = entry[1]
      }
    }
    requestPath = body ? body['?path'] : '';
    const files = await fetchFiles(requestPath, null, body.passwd);
    return new Response(files, {
      headers: {
        'content-type': 'application/json; charset=utf-8',
        'Access-Control-Allow-Origin': '*'
      }
    })
  }
}

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


const clientId = [
  '4da3e7f2-bf6d-467c-aaf0-578078f0bf7c',
  '04c3ca0b-8d07-4773-85ad-98b037d25631'

]
const clientSecret = [
  '7/+ykq2xkfx:.DWjacuIRojIaaWL0QI6',
  'h8@B7kFVOmj0+8HKBWeNTgl@pU/z4yLB'
]

const oauthHost = [
  'https://login.microsoftonline.com',
  'https://login.partner.microsoftonline.cn'
]

const apiHost = [
  'https://graph.microsoft.com',
  'https://microsoftgraph.chinacloudapi.cn'
]

const OAUTH = {
  'redirectUri': 'https://scfonedrive.github.io',
  'refreshToken': ONEDRIVE_REFRESHTOKEN,
  'clientId': clientId[IS_CN],
  'clientSecret': clientSecret[IS_CN],
  'oauthUrl': oauthHost[IS_CN] + '/common/oauth2/v2.0/',
  'apiUrl': apiHost[IS_CN] + '/v1.0/me/drive/root',
  'scope': apiHost[IS_CN] + '/Files.ReadWrite.All offline_access'
}

async function gatherResponse(response) {
  const { headers } = response
  const contentType = headers.get('content-type')
  if (contentType.includes('application/json')) {
    return await response.json()
  } else if (contentType.includes('application/text')) {
    return await response.text()
  } else if (contentType.includes('text/html')) {
    return await response.text()
  } else {
    return await response.text()
  }
}

async function getContent(url) {
  const response = await fetch(url)
  const result = await gatherResponse(response)
  return result
}

async function getContentWithHeaders(url, headers) {
  const response = await fetch(url, { headers: headers })
  const result = await gatherResponse(response)
  return result
}

async function fetchFormData(url, data) {
  const formdata = new FormData();
  for (const key in data) {
    if (data.hasOwnProperty(key)) {
      formdata.append(key, data[key])
    }
  }
  const requestOptions = {
    method: 'POST',
    body: formdata
  };
  const response = await fetch(url, requestOptions)
  const result = await gatherResponse(response)
  return result
}

async function fetchAccessToken() {
  url = OAUTH['oauthUrl'] + 'token'
  data = {
    'client_id': OAUTH['clientId'],
    'client_secret': OAUTH['clientSecret'],
    'grant_type': 'refresh_token',
    'requested_token_use': 'on_behalf_of',
    'refresh_token': OAUTH['refreshToken']
  }
  const result = await fetchFormData(url, data)
  return result.access_token
}

async function fetchFiles(path, fileName, passwd) {
  if (!path || path === '/') {
    if (EXPOSE_PATH === '') {
      path = ''
    } else {
      path = ':' + EXPOSE_PATH
    }
  } else {
    if (EXPOSE_PATH === '') {
      path = ':' + path
    } else {
      path = ':' + EXPOSE_PATH + path
    }
  }

  const accessToken = await fetchAccessToken()
  const uri = OAUTH.apiUrl + encodeURI(path) + '?expand=children(select=name,size,parentReference,lastModifiedDateTime,@microsoft.graph.downloadUrl)'

  const body = await getContentWithHeaders(uri, {
    Authorization: 'Bearer ' + accessToken
  })
  if (fileName) {
    let thisFile = null
    body.children.forEach(file => {
      if (file.name === decodeURIComponent(fileName)) {
        thisFile = file['@microsoft.graph.downloadUrl']
        return
      }
    })
    return thisFile
  } else {
    let files = []
    let encrypted = false
    for (let i = 0; i < body.children.length; i++) {
      const file = body.children[i]
      if (file.name === '.password') {
        const PASSWD = await getContent(file['@microsoft.graph.downloadUrl'])
        if (PASSWD !== passwd) {
          encrypted = true;
          break
        } else {
          continue
        }
      }
      files.push({
        name: file.name,
        size: file.size,
        time: file.lastModifiedDateTime,
        url: file['@microsoft.graph.downloadUrl']
      })
    }
    let parent
    if (body.children.length) {
      parent = body.children[0].parentReference.path
    } else {
      parent = body.parentReference.path
    }
    parent = parent.split(':').pop().replace(EXPOSE_PATH, '') || '/'
    parent = decodeURIComponent(parent)
    if (encrypted) {
      return JSON.stringify({ parent: parent, files: [], encrypted: true })
    } else {
      return JSON.stringify({ parent: parent, files: files })
    }
  }
}

 

9、再新建一个项目(步骤6),并在里面粘贴前端代码,前端代码地址

https://github.com/ytorpedol/FODI/blob/cf-first-index/cf-first-index.js

这是基于项目作者的代码

https://github.com/vcheckzen/FODI/blob/master/front-end/index.html

修改的,因为我觉得利用github pages来显示前端较为复杂,所以在作者基础上修改成cloudflareworkers支持的代码。

10、修改SCF_GATEWAY:SCF 云函数网关地址和SITE_NAME:站点名称,云函数网关填写第10步保存的项目地址,站点名随便(注意:如果你修改了后端的地址,此处也要进行修改,保证SCF_GATEWAY和后端地址一致就可以了),保存部署,访问前端部署地址看看效果。

async function handleRequest(request) {
  const init = {
    headers: {
      'content-type': 'text/html;charset=UTF-8',
    },
  }
  return new Response(renderHTML(), init);
}
addEventListener('fetch', event => {
  return event.respondWith(handleRequest(event.request))
})


function renderHTML() {
  return `<!DOCTYPE html>
<head>
    <script>
        window.GLOBAL_CONFIG = {
            SCF_GATEWAY: "后端项目地址",
            SITE_NAME: "站点名称",
            IS_CF: true
        };
        if (window.GLOBAL_CONFIG.SCF_GATEWAY.indexOf('workers') === -1) {
            window.GLOBAL_CONFIG.SCF_GATEWAY += '/fodi/';
            window.GLOBAL_CONFIG.IS_CF = false;
        }
        // if (location.protocol === 'http:') {
        //     location.href = location.href.replace(/http/, 'https');
        // }
    </script>
    <meta charset="utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <script src="//s0.pstatp.com/cdn/expire-1-M/ionicons/4.5.6/ionicons.js"></script>
    <script src="//s0.pstatp.com/cdn/expire-1-M/marked/0.6.2/marked.min.js"></script>
    <script src="//s0.pstatp.com/cdn/expire-1-M/highlight.js/9.15.6/highlight.min.js"></script>
    <link href="//s0.pstatp.com/cdn/expire-1-M/highlight.js/9.15.6/styles/github.min.css" rel="stylesheet" />
    <link href="//s0.pstatp.com/cdn/expire-1-M/github-markdown-css/3.0.1/github-markdown.min.css" rel="stylesheet" />
    <script src="//s0.pstatp.com/cdn/expire-1-M/jquery/3.4.0/jquery.min.js"></script>
    <script src="//s0.pstatp.com/cdn/expire-1-M/fancybox/3.5.7/jquery.fancybox.min.js"></script>
    <link href="//s0.pstatp.com/cdn/expire-1-M/fancybox/3.5.7/jquery.fancybox.min.css" rel="stylesheet" />
    <style>
        .password-wrapper {
            display: flex;
            align-items: center;
        }

        .password {
            margin: 0 auto;
            padding-top: 1em;
            display: none;
        }

        .password input {
            height: 2em;
            outline: none;
            border: solid rgb(218, 215, 215) 1px;
        }

        .password button {
            background: white;
            height: 2em;
            outline: none;
            border: solid rgb(218, 215, 215) 1px;
        }

        .password button:hover {
            color: white;
            background: rgb(218, 215, 215);
        }

        pre * {
            font-family: Courier New;
        }

        .preview {
            display: none;
            font-size: .8em;
        }

        .content {
            clear: both;
            padding: 0 1em;
            margin: 0 auto;
            text-align: center;
        }

        .file-name {
            line-height: 1em;
            padding: 1em 1em 0;
            text-align: center;
            white-space: nowrap
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值