说明
本项目是OneIndex
的阿里云函数计算版本,无需拥有服务器,即可拥有属于自己的OneDrive
云盘。
特点
Onedrive
网页版实现的一种方式。几乎无成本,完全实现按使用量(访问量)付费,费率极低,并且无固定费用支出。- 不需要自行管理服务器,运行极其稳定。而且服务的质量不受配置(带宽、内存、硬盘……)的影响。极为方便地搭配其他云计算产品进行优化。比如使用
CDN
对静态页面加速,同时降低流量成本。 Onindex-Serverless
被设计成了前后端完全分离的现代网站模式,其中后端是由python
的Flask
框架完成,前端则是React+Dva+Antd
的经典模式。整个项目的部署包仅仅只有3个文件,可以充分减小部署的麻烦,同时提供极大的自由。- 后端:main.py
- 前端:config.json,index.html
- 函数计算使用的是阿里云自己的Docker,在性能上足以满足需求,同时提供了非常稳定的服务。最重要的是有一个非常方便的
http 触发器
,不需要关心域名、回源这些麻烦事,真正做到了开箱即用。
项目地址
Github地址:https://github.com/LiuChangFreeman/OneIndexServerless
后端部分部署步骤
- 注册阿里云账号,获取
AccessKey
和AccessKeySecret
- 开通对象存储与函数计算服务
- 创建一个存储桶和一个云函数,上传
Flask
程序,并填写必要的配置
前端部分部署步骤 - 将函数计算的
http触发器url
填写到config.json
中 - 使用任意一种部署静态资源的方法将
index.html
和config.json
部署到网络上 - 在浏览器中访问
index.html
,并在后台登录OneDrive
账号
部署阿里云对象存储 OSS
1. 注册阿里云账号,开通“函数计算”和“对象存储”两个服务
2. 获取阿里云账号 AccessKey 和 AccessKeySecret
点击右上角主账号
为了安全起见,最好开一个只有对象存储权限的子账户
点击创建用户,比如登录名称为 oss
。点击添加权限。
添加权限 AliyunOSSFullAccess
在认证管理页面,点击创建 AccessKey,记下生成的 AccessKey
和 AccessKeySecret
3. 创建一个存储桶
在对象存储 OSS中,点击创建 Bucket,创建一个存储桶。
Bucket 名称
比如是 onedrive-5iehome
,地域
比如是 华北2(北京)
,其余使用默认配置即可。
请记住 Bucket 名称
和地域
,在下一步开通函数计算服务时需要在同一区域。
在创建完存储桶后,请记下区域节点的 Endpoint(地域节点)
至此,阿里云对象存储 OSS 完成设置。
部署函数计算后端
1. 创建一个函数计算服务
选择函数计算 FC,选择服务及函数,点击创建服务。创建一个函数计算服务。
注意:区域与上一步对象存储为同一区域,即示例中的 华北2(北京)
。
名称比如是 onedirve
。选择显示高级选项,允许函数访问公网选择是。
点击刚刚创建的函数服务 onedrive
,点击创建函数。
创建函数方式选择使用自定义运行时创建,即基于 Python Flask
框架编写程序。函数名称比如是 onedrive
,请求处理程序类型选择处理 HTTP 请求。函数代码运行环境选择Python 3.9,代码上传方式选择使用示例代码。其他选择默认即可。
2. 配置函数计算服务
点击刚刚创建的函数,函数配置如下图所示。注意 Initializer 回调程序为 main.initializer
。
点击函数代码页面,WebIDE
中粘贴 Github
仓库路径 OneIndexServerless/Deploy/Back/
下 main.py
的内容。
# -*- coding: utf-8 -*-
from __future__ import print_function
import os
import oss2
import requests
import urllib
import json
import base64
import time
from flask import Flask,request,redirect
#以下按需更改
password="123456"#后台管理的密码
url_host= ""
#http触发器的接口url
access_key=''#云账号的AccessKey
access_key_secret=''#云账号AccessKey的密码
oss_end_point= ''#访问对象存储的endpoint
oss_bucket_name=''#可以使用的对象存储桶名称
#以下可以不修改
path_oss_store= "oneindex-serverless"#在存储桶中创建的文件夹名称
filename_token= "token.json"#保存凭据的文件名
items_per_page=50#每次获取的项目数量
#以下请勿更改
app = Flask(__name__)
app.secret_key = 'oneindex-serverless'
client_id = '0375f766-e528-4748-91e2-7d8fcb702889'
client_secret = 'vXOJL93{#?xnotilNIU895:'
redirect_uri_register = 'https://oneindex-serverless.github.io/redirect'
redirect_uri_final = '{}/login/authorized'.format(url_host.strip("/"))
auth = oss2.Auth(access_key,access_key_secret)
bucket = oss2.Bucket(auth, oss_end_point, oss_bucket_name)
base_url='https://graph.microsoft.com/v1.0/'
scopes= "offline_access files.read.all"
select="id,name,size,folder,image,video,lastModifiedDateTime"
token=None
oss_available=False
def initializer(context):
init()
def handler(environ, start_response):
return app(environ, start_response)
@app.route('/')
def home():
if token==None or "account" not in token:
data = {
"success":False,
"oss_available":oss_available
}
else:
data = {
"success": True,
"account":token["account"],
"oss_available":oss_available,
}
return json.dumps(data)
@app.route('/verify')
def verify():
code=request.args.get("code")
code=base64.b64decode(code).decode("utf-8")
if code==password:
data={
"success":True
}
else:
data={
"success":False
}
return json.dumps(data)
@app.route('/login')
def login():
code=request.args.get("code")
code=base64.b64decode(code).decode("utf-8")
final=request.args.get("final")
if code==password:
url_login="https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={}&scope={}&response_type=code&redirect_uri={}&state={}".format(client_id, urllib.parse.quote(scopes), redirect_uri_register, redirect_uri_final+"*"+final)
return redirect(url_login)
@app.route('/login/authorized')
def authorized():
global token
try:
code=request.args.get("code")
final = request.args.get("final")
url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
form = "client_id={}&redirect_uri={}&client_secret={}&code={}&grant_type=authorization_code".format(client_id,redirect_uri_register,client_secret,code)
token = requests.post(url, headers=headers, data=form).json()
token["time"] = time.time()
path = "me/drive"
url = base_url + path
access_token = token["access_token"]
headers = {
"Authorization": "bearer {}".format(access_token),
"Content-Type": "application/json"
}
me = requests.get(url, headers=headers).json()
try:
token["account"]=me["owner"]["user"]["email"]
except:
token["account"] = me["owner"]["user"]["displayName"]
token["drive"] = me["id"]
json_token = json.dumps(token, ensure_ascii=False, indent=4)
path_token = os.path.join(path_oss_store, filename_token)
bucket.put_object(path_token, json_token)
bucket.put_object_acl(path_token,oss2.OBJECT_ACL_PRIVATE)
return redirect(final)
except Exception as e:
result={"error":e.message,"token":token,"code":code,"final":final}
return json.dumps(result)
@app.route('/list', methods = ["GET","POST"])
def list():
try:
drive = token["drive"]
access_token = token["access_token"]
headers={
"Authorization":"bearer {}".format(access_token),
"Content-Type":"application/json"
}
if request.method=="POST":
data=request.get_data(as_text=True)
data = json.loads(data)
url=data["next"]
else:
path=request.values.get("path")
if path:
path = "drives/{}/root:/{}:/children".format(drive, path)
else:
path = "me/drive/root/children"
url = base_url + path
url = "{}?$top={}&$select={}".format(url, items_per_page, select)
data = requests.get(url, headers=headers).json()
response={}
items=[]
list=data["value"]
for item in list:
result={}
if "folder" in item:
result["type"]="folder"
result["childCount"]=item["folder"]["childCount"]
elif "image" in item:
result["type"]="picture"
elif "video" in item:
result["type"] = "play-square"
else:
result["type"] = "file"
result["id"] = item["id"]
result["name"] = item["name"]
result["size"] = item["size"]
result["time"] = item["lastModifiedDateTime"]
items.append(result)
response["data"]=items
if "@odata.nextLink" in data:
response["next"]=data["@odata.nextLink"]
else:
response["next"] =None
except Exception as e:
response={"error":e.message,"data":data}
return json.dumps(response)
@app.route('/download')
def download():
id = request.args.get("id")
if id:
path='me/drive/items/{}'.format(id)
url = base_url + path
access_token = token["access_token"]
headers = {
"Authorization": "bearer {}".format(access_token),
"Content-Type": "application/json"
}
data = requests.get(url, headers=headers).json()
if not "folder" in data:
url_download=data["@microsoft.graph.downloadUrl"]
return redirect(url_download)
@app.before_request
def before(*args,**kwargs):
global token
try:
time_now = time.time()
time_last = token["time"]
if time_now - time_last >3500:
refresh_token = token["refresh_token"]
scope = token["scope"]
url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"client_id": client_id,
"client_secret": client_secret,
"redirect_uri": redirect_uri_register,
"refresh_token": refresh_token,
"grant_type": "refresh_token",
"scope": scope,
}
data = requests.post(url, data=data, headers=headers).json()
data["time"] = time.time()
data["account"] = token["account"]
data["drive"] = token["drive"]
token = data
json_token = json.dumps(data, ensure_ascii=False, indent=4)
path_token = os.path.join(path_oss_store, filename_token)
bucket.put_object(path_token, json_token)
except:
pass
def init():
global token,oss_available
try:
service = oss2.Service(auth, oss_end_point.replace("http://", ""), connect_timeout=1)
service.list_buckets()
oss_available=True
path_token ="{}/{}".format(path_oss_store,filename_token)
if bucket.object_exists(path_token):
token = bucket.get_object(path_token)
token = json.loads(token.read())
except:
pass
if __name__=="__main__":
init()
app.run()
更改下面内容为刚刚创建的 AdccessKey
,AccessSecret
,oss_end_point
和存储桶名称。点击部署代码。
#以下按需更改
password="123456"#后台管理的密码
url_host= ""
#http触发器的接口url
access_key='xxx'#云账号的AccessKey
access_key_secret='xxx'#云账号AccessKey的密码
oss_end_point= 'http://oss-cn-beijing-internal.aliyuncs.com'#访问对象存储的endpoint
oss_bucket_name='onedrive-5iehome'#可以使用的对象存储桶名称
点击触发器管理(URL)页面,创建触发器 onedrive
并启用。复制公网访问地址
链接,将其粘贴到上面函数代码 main.py
的 url_host
中。
部署前端网页
1. 复制前端网页内容到服务器
将Github
仓库路径 OneIndexServerless/Deploy/Front/
下 config.json
和 index.html
复制到网站根目录或指定文件夹下。
2. 配置相应文件
config.json
配置内容如下:
{
"host":"https://1379413538033051.cn-beijing.fc.aliyuncs.com/2016-08-15/proxy/onedrive/onedrive"
}
至此该项目搭建完毕。
部署文件下载地址:
Onindex-Serverless搭建OneIndex阿里云函数版本.exe
https://www.aliyundrive.com/s/qBN2GQpAVzs 提取码: 09bn
https://www.123pan.com/s/Oy5RVv-GwXB.html 提取码:uHA5
登录后台配置 OneDrive 账号
1. 注册 OneDrive 5T账号
Office 365 E5账号
注册地址:https://developer.microsoft.com/en-us/microsoft-365/dev-program
如何申请上网搜索,这里就不赘述了。稍后我也可以写个教程。
开通权限请参考:https://alist.nn.ci/zh/guide/drivers/onedrive.html
2. 登录 OneIndex 后台配置
-
打开后台地址,默认是 `https://www.网站.com/oneindex/#/admin
-
输入密码(与部署函数计算阶段的一致,默认是
123456
) -
登录
OneDrive
账号,会自动跳转到MicroSoft
账号网站。最终显示系统状态全部是绿色即为安装成功。
-
效果展示
参考资料
【END】