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