mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-14 13:06:51 +00:00
fix(storage): 修复类型检查暴露的三处缺陷(含 OSS 上传运行时 bug)
basedpyright 报出的三处,逐条定位后发现都不是「标注问题」:
1) oss_adapter.py:70 —— 真运行时 bug
SDK 里 `Client.uploader` 是**方法**(`def uploader(self, **kwargs) -> Uploader`),
原写法 `self.client.uploader.upload_file(...)` 是在绑定方法上取属性,运行时必抛
AttributeError —— 即 OSS 上传链路从未成功过(生产未配 OSS 源,故未暴露)。
已改为 `self.client.uploader().upload_file(...)`,并按 SDK 签名核对
`upload_file(request, filepath, part_size=..., parallel_num=...)` 参数正确。
2) sftp_adapter.py:45 —— 缺主机校验
`config.host` 为 `str | None`(对象存储可不填),却直接传给 paramiko
`connect(hostname: str)`。已把 host 并入既有的用户名/密码守卫,
缺主机时返回业务异常 400「SFTP 存储源必须配置主机、用户名与密码」。
3) ftp_ftps_adapter.py:58 —— 同上
`_new_client()` 增加主机守卫(400「FTP 存储源必须配置主机地址」,并补 CustomException 导入);
顺带修复同文件 `_ImplicitFTP_TLS.connect` 覆写签名与 ftplib.FTP 不兼容
(timeout: float、source_address: tuple[str, int] | None),消除该文件最后一条 error。
验证:basedpyright 这三个文件 0 errors(修复前 4 条);ruff 全绿;pytest 97 passed;
运行时探针:缺主机 → 拦截为 400 业务异常;配主机 → 守卫放行并进入真实连接(ConnectionRefused 如期)。
This commit is contained in:
@@ -5,6 +5,7 @@ import ssl
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import logger
|
||||
from app.modules.task.storage.core.base import BaseStorageAdapter, FtpAdvancedConfig, StorageObject, StorageProtocol
|
||||
|
||||
@@ -12,7 +13,14 @@ from app.modules.task.storage.core.base import BaseStorageAdapter, FtpAdvancedCo
|
||||
class _ImplicitFTP_TLS(ftplib.FTP_TLS):
|
||||
"""隐式 FTPS(默认端口 990)连接子类:socket 直连即套 TLS。"""
|
||||
|
||||
def connect(self, host: str = "", port: int = 0, timeout: int = -999, source_address=None) -> str:
|
||||
def connect(
|
||||
self,
|
||||
host: str = "",
|
||||
port: int = 0,
|
||||
timeout: float = -999,
|
||||
source_address: tuple[str, int] | None = None,
|
||||
) -> str:
|
||||
"""覆写 ``ftplib.FTP.connect``:签名与标准库/类型存根保持一致(implicit FTPS 直连即套 TLS)。"""
|
||||
if host != "":
|
||||
self.host = host
|
||||
if port > 0:
|
||||
@@ -47,6 +55,8 @@ class FtpStorageAdapter(BaseStorageAdapter):
|
||||
|
||||
def _new_client(self) -> ftplib.FTP_TLS | ftplib.FTP:
|
||||
"""建立连接并登录。FTP 明文 / FTPS(显式或隐式 TLS)按配置选择。"""
|
||||
if not self.config.host:
|
||||
raise CustomException(msg="FTP 存储源必须配置主机地址")
|
||||
implicit = False
|
||||
if self.config.protocol == StorageProtocol.FTPS:
|
||||
# 隐式 TLS:implicit_tls 开关或 encrypt_type>=3(兼容两种配置写法)
|
||||
|
||||
@@ -67,7 +67,9 @@ class OssStorageAdapter(BaseStorageAdapter):
|
||||
# 走 SDK 高层分片上传:普通小文件自动单次上传,大文件按端点分片参数并发分片;
|
||||
# 流式传输(stream)时 part_size 为超大值,等效单次上传
|
||||
part_size, concurrency, _ = self._multipart_settings()
|
||||
self.client.uploader.upload_file(
|
||||
# 注意:SDK 的 ``Client.uploader`` 是**方法**(返回 Uploader),必须调用后使用;
|
||||
# 写成 ``self.client.uploader.upload_file(...)`` 会在运行时抛 AttributeError。
|
||||
self.client.uploader().upload_file(
|
||||
oss.PutObjectRequest(bucket=self.bucket_name, key=remote_path),
|
||||
local_path,
|
||||
part_size=part_size,
|
||||
|
||||
@@ -36,8 +36,8 @@ class SftpStorageAdapter(BaseStorageAdapter):
|
||||
"""建立并复用 SFTP 连接:首次使用时连接,之后直接复用。"""
|
||||
if self._sftp is not None:
|
||||
return self._sftp
|
||||
if not self.config.username or not self.config.password:
|
||||
raise CustomException(msg="SFTP 存储源必须配置用户名与密码")
|
||||
if not self.config.host or not self.config.username or not self.config.password:
|
||||
raise CustomException(msg="SFTP 存储源必须配置主机、用户名与密码")
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user