使用CloudFlare Workers 零成本部署 OneDrive 文件列表程序FODI

2020年2月20日 621 次阅读 0 条评论 0 人点赞

这也是忽然而产生的一个想法。最近hk服务器到期,需要迁移自己以前搭建的olaindex和pyone。但是现实不是那么美好,自己已经没有闲置服务器可以用了,我也不想再购买新服务器,因为好多东西,瞎折腾也免不了吃灰的厄运。既花了钱,也没能够增强自己的技术。于是便想着有没有免费的方法。

其实还有Heroku这个工具可以部署onelist。但是我记得用cf可以部署goindex这种谷歌网盘的列表程序。虽然cf已经被玩坏了,cf搭建goindex已经没有速度了。但是onedrive国内可以直接访问,那么就不需要经过cf,应该可以保证一定的速度。那么是不是cf可以部署onedrive的列表程序呢?有了想法,立马去百度 and google 了一下,终于找到了一个程序FODI这个程序,是支持使用云函数部署的。

FODI( Fast OneDrive Index )

  • 接近秒速列表;
  • 指定展示路径;
  • 特定文件夹加密;
  • 无需服务器免费部署;
  • 基本文本、图片、音视频和 Office 三件套预览;

FODI 程序的开源地址如下:

https://github.com/vcheckzen/FODI

但是官网不知什么原因,作者并没有维护。那个部署步骤已经没办法看了。但是幸运的是,vircloud大佬已经写了一个教程,把基本步骤已经写明,于是,我参考大佬的教程,来造这个轮子。

一、获取refresh_token

访问如下网址:

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

点击 Get a refresh_token ,然后使用你的office365账户登录

登录完成后你发现又返回原来的界面。然后需要把地址栏的链接中

.../?authorization_code&code=... 为

.../authorization_code?code=... 并重新访问,修改完后如下。

复制返回的 refresh_token 存储下来。

二、cloudflare后台操作

1.进入cf workers后台,选择新建worker

2.复制如下代码,到worker编辑器中

/**
 * IS_CN: 如果为世纪互联版本,请将 0 改为 1
 * EXPOSE_PATH:暴露路径,如全盘展示请留空,否则按 '/媒体/音乐' 的格式填写
 * ONEDRIVE_REFRESHTOKEN: refresh_token
 */
const IS_CN = 0;
const EXPOSE_PATH = ""
const ONEDRIVE_REFRESHTOKEN = ""


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',
  '[email protected][email protected]/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 })
    }
  }
}

代码就是程序根目录中back-end-cf文件夹中的index.js(链接是文件Github地址)中的内容。然后把保存的 refresh_token 在指定位置修改,至于是不是世纪互联,和想展示哪个目录,请按程序注释修改。

保存生成

然后为如下界面

然后回到主界面

可以改称自己认为比较好的名,亦可以不改。记住这个域名。

三、部署程序主页

把引导页(位于front-end文件夹中的index.html)( 链接为文件的Github地址)部署到你的服务器上,当然也可以部署到Github Pages中,这儿可操作性太强了,我就不再描述了。然后需要修改文件中的SCF_GATEWAY

这儿地址要加上https。

然后访问这个文件就可以,就可以看到这个页面了。这个页面是真的简洁,哈哈哈。没想到的简洁。打开要等几秒,才会显示内容。

几点小注意:(摘自vircloud.net)

  • 支持加密,在需加密的文件夹中新增 .password 文件,编码为 UTF8NoBOM ,内容为密码即可;
  • 后端实际上返回的是 json,所以前端其实是可以自己做一个的。
  • 内容传输时使用的地址是 OneDrive 官方地址,所以速度还是有保证的;
  • Worker 部署完是无法 GET 方式访问的,会提示“Error 1101 Worker threw exception”,不必理会;

菜鸟

文章评论(0)