fix aliyunpan api

This commit is contained in:
jxxghp 2024-06-18 12:01:53 +08:00
parent 90df09e64d
commit 2a333add9b
4 changed files with 210 additions and 49 deletions

View File

@ -22,6 +22,9 @@ def qrcode(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
@router.get("/check", summary="二维码登录确认", response_model=schemas.Response) @router.get("/check", summary="二维码登录确认", response_model=schemas.Response)
def check(ck: str, t: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: def check(ck: str, t: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
二维码登录确认
"""
if not ck or not t: if not ck or not t:
return schemas.Response(success=False, message="参数错误") return schemas.Response(success=False, message="参数错误")
data, errmsg = AliyunHelper().check_login(ck, t) data, errmsg = AliyunHelper().check_login(ck, t)
@ -32,7 +35,14 @@ def check(ck: str, t: str, _: schemas.TokenPayload = Depends(verify_token)) -> A
@router.get("/userinfo", summary="查询用户信息", response_model=schemas.Response) @router.get("/userinfo", summary="查询用户信息", response_model=schemas.Response)
def userinfo(_: schemas.TokenPayload = Depends(verify_token)) -> Any: def userinfo(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
info = AliyunHelper().get_user_info() """
查询用户信息
"""
aliyunhelper = AliyunHelper()
# 浏览一次文件确定token正确性
aliyunhelper.list_files()
# 查询用户信息返回
info = aliyunhelper.get_user_info()
if info: if info:
return schemas.Response(success=True, data=info) return schemas.Response(success=True, data=info)
return schemas.Response(success=False) return schemas.Response(success=False)

View File

@ -1,5 +1,4 @@
import shutil import shutil
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, List from typing import Any, List
@ -8,9 +7,10 @@ from starlette.responses import FileResponse, Response
from app import schemas from app import schemas
from app.core.config import settings from app.core.config import settings
from app.core.security import verify_token, verify_uri_token from app.core.security import verify_token, verify_uri_session
from app.helper.aliyun import AliyunHelper from app.helper.aliyun import AliyunHelper
from app.log import logger from app.log import logger
from app.utils.http import RequestUtils
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.system import SystemUtils from app.utils.system import SystemUtils
@ -102,7 +102,7 @@ def list_path(path: str,
@router.get("/local/listdir", summary="所有目录(本地,不含文件)", response_model=List[schemas.FileItem]) @router.get("/local/listdir", summary="所有目录(本地,不含文件)", response_model=List[schemas.FileItem])
def list_dir(path: str, _: schemas.TokenPayload = Depends(verify_uri_token)) -> Any: def list_dir(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
查询当前目录下所有目录 查询当前目录下所有目录
""" """
@ -174,7 +174,7 @@ def delete(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
@router.get("/local/download", summary="下载文件(本地)") @router.get("/local/download", summary="下载文件(本地)")
def download(path: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: def download(path: str, _: schemas.TokenPayload = Depends(verify_uri_session)) -> Any:
""" """
下载文件或目录 下载文件或目录
""" """
@ -210,7 +210,7 @@ def rename(path: str, new_name: str, _: schemas.TokenPayload = Depends(verify_to
@router.get("/local/image", summary="读取图片(本地)") @router.get("/local/image", summary="读取图片(本地)")
def image(path: str, _: schemas.TokenPayload = Depends(verify_uri_token)) -> Any: def image(path: str, _: schemas.TokenPayload = Depends(verify_uri_session)) -> Any:
""" """
读取图片 读取图片
""" """
@ -230,12 +230,14 @@ def image(path: str, _: schemas.TokenPayload = Depends(verify_uri_token)) -> Any
@router.get("/aliyun/list", summary="所有目录和文件(阿里云盘)", response_model=List[schemas.FileItem]) @router.get("/aliyun/list", summary="所有目录和文件(阿里云盘)", response_model=List[schemas.FileItem])
def list_path(path: str, def list_path(path: str,
fileid: str, fileid: str,
filetype: str,
sort: str = 'updated_at', sort: str = 'updated_at',
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
查询当前目录下所有目录和文件 查询当前目录下所有目录和文件
:param path: 当前路径 :param path: 当前路径
:param fileid: 文件ID :param fileid: 文件ID
:param filetype: 文件类型
:param sort: 排序方式name:按名称排序time:按修改时间排序 :param sort: 排序方式name:按名称排序time:按修改时间排序
:param _: token :param _: token
:return: 所有目录和文件 :return: 所有目录和文件
@ -246,6 +248,20 @@ def list_path(path: str,
path = "/" path = "/"
if sort == "time": if sort == "time":
sort = "updated_at" sort = "updated_at"
if filetype == "file":
fileinfo = AliyunHelper().get_file_detail(fileid)
if fileinfo:
return [schemas.FileItem(
fileid=fileinfo.get("file_id"),
parent_fileid=fileinfo.get("parent_file_id"),
type="file",
path=f"{path}{fileinfo.get('name')}",
name=fileinfo.get("name"),
size=fileinfo.get("size"),
extension=fileinfo.get("file_extension"),
modify_time=StringUtils.str_to_timestamp(fileinfo.get("updated_at"))
)]
return []
items = AliyunHelper().list_files(parent_file_id=fileid, order_by=sort) items = AliyunHelper().list_files(parent_file_id=fileid, order_by=sort)
if not items: if not items:
return [] return []
@ -253,7 +269,7 @@ def list_path(path: str,
fileid=item.get("file_id"), fileid=item.get("file_id"),
parent_fileid=item.get("parent_file_id"), parent_fileid=item.get("parent_file_id"),
type="dir" if item.get("type") == "folder" else "file", type="dir" if item.get("type") == "folder" else "file",
path=f"{path}{item.get('name')}/", path=f"{path}{item.get('name')}" + "/" if item.get("type") == "folder" else "",
name=item.get("name"), name=item.get("name"),
size=item.get("size"), size=item.get("size"),
extension=item.get("file_extension"), extension=item.get("file_extension"),
@ -261,56 +277,48 @@ def list_path(path: str,
) for item in items] ) for item in items]
@router.get("/aliyun/listdir", summary="所有目录(阿里云盘,不含文件)", response_model=List[schemas.FileItem])
def list_dir(path: str,
fileid: str,
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
查询当前目录下所有目录
"""
if not fileid:
return []
if not path:
path = "/"
@router.get("/aliyun/mkdir", summary="创建目录(阿里云盘)", response_model=schemas.Response) @router.get("/aliyun/mkdir", summary="创建目录(阿里云盘)", response_model=schemas.Response)
def mkdir(path: str, def mkdir(fileid: str,
fileid: str, name: str,
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
创建目录 创建目录
""" """
if not fileid: if not fileid or not name:
return schemas.Response(success=False) return schemas.Response(success=False)
if not path: result = AliyunHelper().create_folder(parent_file_id=fileid, name=name)
path = "/" if result:
return schemas.Response(success=True)
return schemas.Response(success=False)
@router.get("/aliyun/delete", summary="删除文件或目录(阿里云盘)", response_model=schemas.Response) @router.get("/aliyun/delete", summary="删除文件或目录(阿里云盘)", response_model=schemas.Response)
def delete(path: str, def delete(fileid: str,
fileid: str,
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
删除文件或目录 删除文件或目录
""" """
if not fileid: if not fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
if not path: result = AliyunHelper().delete_file(fileid)
path = "/" if result:
return schemas.Response(success=True)
return schemas.Response(success=False)
@router.get("/aliyun/download", summary="下载文件(阿里云盘)") @router.get("/aliyun/download", summary="下载文件(阿里云盘)")
def download(path: str, def download(fileid: str,
fileid: str, _: schemas.TokenPayload = Depends(verify_uri_session)) -> Any:
_: schemas.TokenPayload = Depends(verify_uri_token)) -> Any:
""" """
下载文件或目录 下载文件或目录
""" """
if not fileid: if not fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
if not path: url = AliyunHelper().get_download_url(fileid)
path = "/" if url:
# 重定向
return Response(status_code=302, headers={"Location": url})
return schemas.Response(success=False)
@router.get("/aliyun/rename", summary="重命名文件或目录(阿里云盘)", response_model=schemas.Response) @router.get("/aliyun/rename", summary="重命名文件或目录(阿里云盘)", response_model=schemas.Response)
@ -320,12 +328,21 @@ def rename(fileid: str, new_name: str, _: schemas.TokenPayload = Depends(verify_
""" """
if not fileid or not new_name: if not fileid or not new_name:
return schemas.Response(success=False) return schemas.Response(success=False)
result = AliyunHelper().rename_file(fileid, new_name)
if result:
return schemas.Response(success=True)
return schemas.Response(success=False)
@router.get("/aliyun/image", summary="读取图片(阿里云盘)", response_model=schemas.Response) @router.get("/aliyun/image", summary="读取图片(阿里云盘)", response_model=schemas.Response)
def image(fileid: str, _: schemas.TokenPayload = Depends(verify_uri_token)) -> Any: def image(fileid: str, _: schemas.TokenPayload = Depends(verify_uri_session)) -> Any:
""" """
读取图片 读取图片
""" """
if not fileid: if not fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
url = AliyunHelper().get_download_url(fileid)
if url:
# 重定向
return Response(status_code=302, headers={"Location": url})
return schemas.Response(success=False)

View File

@ -87,6 +87,18 @@ def verify_uri_token(token: str = Depends(get_token)) -> str:
return token return token
def verify_uri_session(token: str = Depends(get_token)) -> str:
"""
通过依赖项使用token进行身份认证
"""
if not verify_token(token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="token校验不通过"
)
return token
def verify_uri_apikey(apikey: str = Depends(get_apikey)) -> str: def verify_uri_apikey(apikey: str = Depends(get_apikey)) -> str:
""" """
通过依赖项使用apikey进行身份认证 通过依赖项使用apikey进行身份认证

View File

@ -39,11 +39,21 @@ class AliyunHelper:
user_info_url = "https://user.aliyundrive.com/v2/user/get" user_info_url = "https://user.aliyundrive.com/v2/user/get"
# 浏览文件 # 浏览文件
list_file_url = "https://api.aliyundrive.com/adrive/v3/file/list" list_file_url = "https://api.aliyundrive.com/adrive/v3/file/list"
# 创建目录
create_folder_url = "https://api.aliyundrive.com/adrive/v2/file/createWithFolders"
# 文件详情
file_detail_url = "https://api.aliyundrive.com/v2/file/get"
# 删除文件
delete_file_url = " https://api.aliyundrive.com/v2/recyclebin/trash"
# 文件重命名
rename_file_url = "https://api.aliyundrive.com/v3/file/update"
# 获取下载链接
download_url = "https://api.aliyundrive.com/v2/file/get_download_url"
def __init__(self): def __init__(self):
self.systemconfig = SystemConfigOper() self.systemconfig = SystemConfigOper()
def __log_error(self, res: Response, apiname: str): def __handle_error(self, res: Response, apiname: str, action: bool = True):
""" """
统一处理和打印错误信息 统一处理和打印错误信息
""" """
@ -56,9 +66,15 @@ class AliyunHelper:
display_message = result.get("display_message") display_message = result.get("display_message")
if code or message: if code or message:
logger.warn(f"Aliyun {apiname}失败:{code} - {display_message or message}") logger.warn(f"Aliyun {apiname}失败:{code} - {display_message or message}")
if code == "DeviceSessionSignatureInvalid": if action:
logger.warn("设备会话签名无效,请重新扫码登录!") if code in ["UserDeviceOffline", "DeviceSessionSignatureInvalid"]:
self.clear_params() logger.warn("设备已下线或无效,正在重新建立会话...")
self.create_session(self.get_headers(self.auth_params))
if code == "AccessTokenInvalid":
logger.warn("访问令牌已失效,正在刷新令牌...")
self.__update_accesstoken(self.auth_params, self.auth_params.get("refreshToken"))
else:
logger.info(f"Aliyun {apiname}成功")
@property @property
def auth_params(self): def auth_params(self):
@ -94,7 +110,7 @@ class AliyunHelper:
"t": data.get("t") "t": data.get("t")
}, "" }, ""
elif res is not None: elif res is not None:
self.__log_error(res, "生成二维码") self.__handle_error(res, "生成二维码")
return {}, f"请求阿里云盘二维码失败:{res.status_code} - {res.reason}" return {}, f"请求阿里云盘二维码失败:{res.status_code} - {res.reason}"
return {}, f"请求阿里云盘二维码失败:无法连接!" return {}, f"请求阿里云盘二维码失败:无法连接!"
@ -158,16 +174,17 @@ class AliyunHelper:
return {}, f"bizExt 解码失败:{str(e)}" return {}, f"bizExt 解码失败:{str(e)}"
return data, "" return data, ""
elif res is not None: elif res is not None:
self.__log_error(res, "登录确认") self.__handle_error(res, "登录确认")
return {}, f"阿里云盘登录确认失败:{res.status_code} - {res.reason}" return {}, f"阿里云盘登录确认失败:{res.status_code} - {res.reason}"
return {}, "阿里云盘登录确认失败:无法连接!" return {}, "阿里云盘登录确认失败:无法连接!"
def __update_accesstoken(self, refresh_token: str) -> bool: def __update_accesstoken(self, params: dict, refresh_token: str) -> bool:
""" """
更新阿里云盘访问令牌 更新阿里云盘访问令牌
""" """
res = RequestUtils(headers={"Content-Type": "application/json"}, timeout=10).post_res( headers = self.get_headers(params)
self.update_accessstoken_url, data={ res = RequestUtils(headers=headers, timeout=10).post_res(
self.update_accessstoken_url, json={
"refresh_token": refresh_token, "refresh_token": refresh_token,
"grant_type": "refresh_token" "grant_type": "refresh_token"
}) })
@ -176,6 +193,7 @@ class AliyunHelper:
code = data.get("code") code = data.get("code")
if code in ["RefreshTokenExpired", "InvalidParameter.RefreshToken"]: if code in ["RefreshTokenExpired", "InvalidParameter.RefreshToken"]:
logger.warn("刷新令牌已过期,请重新登录!") logger.warn("刷新令牌已过期,请重新登录!")
self.clear_params()
return False return False
self.update_params({ self.update_params({
"accessToken": data.get('access_token'), "accessToken": data.get('access_token'),
@ -185,7 +203,7 @@ class AliyunHelper:
logger.info(f"阿里云盘访问令牌已更新accessToken={data.get('access_token')}") logger.info(f"阿里云盘访问令牌已更新accessToken={data.get('access_token')}")
return True return True
else: else:
self.__log_error(res, "更新令牌") self.__handle_error(res, "更新令牌", action=False)
return False return False
def create_session(self, headers: dict): def create_session(self, headers: dict):
@ -208,7 +226,7 @@ class AliyunHelper:
'modelName': __os_name(), 'modelName': __os_name(),
'pubKey': self._X_PUBLIC_KEY, 'pubKey': self._X_PUBLIC_KEY,
}) })
self.__log_error(res, "创建会话") self.__handle_error(res, "创建会话", action=False)
def get_access_params(self) -> Optional[dict]: def get_access_params(self) -> Optional[dict]:
""" """
@ -225,15 +243,24 @@ class AliyunHelper:
logger.warn("阿里云盘访问令牌参数错误,请重新扫码登录!") logger.warn("阿里云盘访问令牌参数错误,请重新扫码登录!")
self.clear_params() self.clear_params()
return None return None
# 是否需要更新设备信息
update_device = False
# 判断访问令牌是否过期
if (time.time() - update_time) >= expires_in: if (time.time() - update_time) >= expires_in:
logger.info("阿里云盘访问令牌已过期,正在更新...") logger.info("阿里云盘访问令牌已过期,正在更新...")
if not self.__update_accesstoken(refresh_token): if not self.__update_accesstoken(params, refresh_token):
# 更新失败
return None return None
update_device = True
# 生成设备ID
x_device_id = params.get("x_device_id") x_device_id = params.get("x_device_id")
if not x_device_id: if not x_device_id:
x_device_id = uuid.uuid4().hex x_device_id = uuid.uuid4().hex
params['x_device_id'] = x_device_id params['x_device_id'] = x_device_id
self.update_params({"x_device_id": x_device_id}) self.update_params({"x_device_id": x_device_id})
update_device = True
# 更新设备信息重新创建会话
if update_device:
self.create_session(self.get_headers(params)) self.create_session(self.get_headers(params))
return params return params
@ -271,7 +298,7 @@ class AliyunHelper:
}) })
return result return result
else: else:
self.__log_error(res, "获取用户信息") self.__handle_error(res, "获取用户信息")
return {} return {}
def list_files(self, parent_file_id: str = 'root', list_type: str = None, def list_files(self, parent_file_id: str = 'root', list_type: str = None,
@ -320,6 +347,101 @@ class AliyunHelper:
# 没有下一页 # 没有下一页
break break
else: else:
self.__log_error(res, "浏览文件") self.__handle_error(res, "浏览文件")
break break
return ret_items return ret_items
def create_folder(self, parent_file_id: str, name: str) -> bool:
"""
创建目录
"""
params = self.get_access_params()
if not params:
return False
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.create_folder_url, json={
"drive_id": params.get("resourceDriveId"),
"parent_file_id": parent_file_id,
"name": name,
"check_name_mode": "refuse",
"type": "folder"
})
if res:
return True
else:
self.__handle_error(res, "创建目录")
return False
def delete_file(self, file_id: str) -> bool:
"""
删除文件
"""
params = self.get_access_params()
if not params:
return False
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.delete_file_url, json={
"drive_id": params.get("resourceDriveId"),
"file_id": file_id
})
if res:
return True
else:
self.__handle_error(res, "删除文件")
return False
def get_file_detail(self, file_id: str) -> Optional[dict]:
"""
获取文件详情
"""
params = self.get_access_params()
if not params:
return None
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.file_detail_url, json={
"drive_id": params.get("resourceDriveId"),
"file_id": file_id
})
if res:
return res.json()
else:
self.__handle_error(res, "获取文件详情")
return None
def rename_file(self, file_id: str, name: str) -> bool:
"""
重命名文件
"""
params = self.get_access_params()
if not params:
return False
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.rename_file_url, json={
"drive_id": params.get("resourceDriveId"),
"file_id": file_id,
"name": name,
"check_name_mode": "refuse"
})
if res:
return True
else:
self.__handle_error(res, "重命名文件")
return False
def get_download_url(self, file_id: str) -> Optional[str]:
"""
获取下载链接
"""
params = self.get_access_params()
if not params:
return None
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.download_url, json={
"drive_id": params.get("resourceDriveId"),
"file_id": file_id
})
if res:
return res.json().get("url")
else:
self.__handle_error(res, "获取下载链接")
return None