Onindex-Serverless:搭建 OneIndex 阿里云函数版本
本文最后更新于 116 天前,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Onindex-Serverless

说明

本项目是OneIndex的阿里云函数计算版本,无需拥有服务器,即可拥有属于自己的OneDrive云盘。

特点

  • Onedrive 网页版实现的一种方式。几乎无成本,完全实现按使用量(访问量)付费,费率极低,并且无固定费用支出。
  • 不需要自行管理服务器,运行极其稳定。而且服务的质量不受配置(带宽、内存、硬盘……)的影响。极为方便地搭配其他云计算产品进行优化。比如使用CDN对静态页面加速,同时降低流量成本。
  • Onindex-Serverless 被设计成了前后端完全分离的现代网站模式,其中后端是由 pythonFlask 框架完成,前端则是 React+Dva+Antd 的经典模式。整个项目的部署包仅仅只有3个文件,可以充分减小部署的麻烦,同时提供极大的自由。
    • 后端:main.py
    • 前端:config.json,index.html
  • 函数计算使用的是阿里云自己的Docker,在性能上足以满足需求,同时提供了非常稳定的服务。最重要的是有一个非常方便的 http 触发器,不需要关心域名、回源这些麻烦事,真正做到了开箱即用。

项目地址

Github地址:https://github.com/LiuChangFreeman/OneIndexServerless

后端部分部署步骤

  • 注册阿里云账号,获取 AccessKeyAccessKeySecret
  • 开通对象存储与函数计算服务
  • 创建一个存储桶和一个云函数,上传 Flask 程序,并填写必要的配置
    前端部分部署步骤
  • 将函数计算的 http触发器url 填写到 config.json
  • 使用任意一种部署静态资源的方法将 index.htmlconfig.json 部署到网络上
  • 在浏览器中访问 index.html,并在后台登录 OneDrive 账号

部署阿里云对象存储 OSS

1. 注册阿里云账号,开通“函数计算”和“对象存储”两个服务

OneIndex-Serverless-01

2. 获取阿里云账号 AccessKey 和 AccessKeySecret

点击右上角主账号

OneIndex-Serverless-02

为了安全起见,最好开一个只有对象存储权限的子账户

OneIndex-Serverless-03

点击创建用户,比如登录名称为 oss。点击添加权限

OneIndex-Serverless-04

添加权限 AliyunOSSFullAccess

OneIndex-Serverless-05

认证管理页面,点击创建 AccessKey,记下生成的 AccessKeyAccessKeySecret

OneIndex-Serverless-06

3. 创建一个存储桶

对象存储 OSS中,点击创建 Bucket,创建一个存储桶。
Bucket 名称 比如是 onedrive-5iehome地域 比如是 华北2(北京),其余使用默认配置即可。
请记住 Bucket 名称地域,在下一步开通函数计算服务时需要在同一区域。

OneIndex-Serverless-07

在创建完存储桶后,请记下区域节点的 Endpoint(地域节点)

OneIndex-Serverless-08

至此,阿里云对象存储 OSS 完成设置。

部署函数计算后端

1. 创建一个函数计算服务

选择函数计算 FC,选择服务及函数,点击创建服务。创建一个函数计算服务。
注意:区域与上一步对象存储为同一区域,即示例中的 华北2(北京)
名称比如是 onedirve。选择显示高级选项允许函数访问公网选择

OneIndex-Serverless-09

点击刚刚创建的函数服务 onedrive,点击创建函数

OneIndex-Serverless-10

创建函数方式选择使用自定义运行时创建,即基于 Python Flask 框架编写程序。函数名称比如是 onedrive请求处理程序类型选择处理 HTTP 请求。函数代码运行环境选择Python 3.9,代码上传方式选择使用示例代码。其他选择默认即可。

OneIndex-Serverless-11

2. 配置函数计算服务

点击刚刚创建的函数,函数配置如下图所示。注意 Initializer 回调程序main.initializer

OneIndex-Serverless-12

OneIndex-Serverless-13

点击函数代码页面,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()

更改下面内容为刚刚创建的 AdccessKeyAccessSecretoss_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.pyurl_host 中。

OneIndex-Serverless-14

OneIndex-Serverless-15

部署前端网页

1. 复制前端网页内容到服务器

Github 仓库路径 OneIndexServerless/Deploy/Front/config.jsonindex.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 后台配置

  1. 打开后台地址,默认是 `https://www.网站.com/oneindex/#/admin

  2. 输入密码(与部署函数计算阶段的一致,默认是123456

  3. 登录 OneDrive 账号,会自动跳转到 MicroSoft 账号网站。最终显示系统状态全部是绿色即为安装成功。

    OneIndex-Serverless-16

  4. 效果展示

    OneIndex-Serverless-17

参考资料

版权归属: E家之长
本文链接: https://www.5iehome.cc/archives/onindex-serverless-for-onedrive.html
许可协议: 本文使用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》协议授权
暂无评论

发送评论 编辑评论


上一篇
下一篇