feat:模块健康检查

This commit is contained in:
jxxghp
2024-03-06 13:23:51 +08:00
parent 01e08c8e69
commit 8cb061ff75
24 changed files with 248 additions and 28 deletions

View File

@ -197,33 +197,6 @@ def get_logging(token: str, length: int = 50, logfile: str = "moviepilot.log"):
return StreamingResponse(log_generator(), media_type="text/event-stream") return StreamingResponse(log_generator(), media_type="text/event-stream")
@router.get("/nettest", summary="测试网络连通性")
def nettest(url: str,
proxy: bool,
_: schemas.TokenPayload = Depends(verify_token)):
"""
测试网络连通性
"""
# 记录开始的毫秒数
start_time = datetime.now()
url = url.replace("{TMDBAPIKEY}", settings.TMDB_API_KEY)
result = RequestUtils(proxies=settings.PROXY if proxy else None,
ua=settings.USER_AGENT).get_res(url)
# 计时结束的毫秒数
end_time = datetime.now()
# 计算相关秒数
if result and result.status_code == 200:
return schemas.Response(success=True, data={
"time": round((end_time - start_time).microseconds / 1000)
})
elif result:
return schemas.Response(success=False, message=f"错误码:{result.status_code}", data={
"time": round((end_time - start_time).microseconds / 1000)
})
else:
return schemas.Response(success=False, message="网络连接失败!")
@router.get("/versions", summary="查询Github所有Release版本", response_model=schemas.Response) @router.get("/versions", summary="查询Github所有Release版本", response_model=schemas.Response)
def latest_version(_: schemas.TokenPayload = Depends(verify_token)): def latest_version(_: schemas.TokenPayload = Depends(verify_token)):
""" """
@ -269,6 +242,53 @@ def ruletest(title: str,
}) })
@router.get("/nettest", summary="测试网络连通性")
def nettest(url: str,
proxy: bool,
_: schemas.TokenPayload = Depends(verify_token)):
"""
测试网络连通性
"""
# 记录开始的毫秒数
start_time = datetime.now()
url = url.replace("{TMDBAPIKEY}", settings.TMDB_API_KEY)
result = RequestUtils(proxies=settings.PROXY if proxy else None,
ua=settings.USER_AGENT).get_res(url)
# 计时结束的毫秒数
end_time = datetime.now()
# 计算相关秒数
if result and result.status_code == 200:
return schemas.Response(success=True, data={
"time": round((end_time - start_time).microseconds / 1000)
})
elif result:
return schemas.Response(success=False, message=f"错误码:{result.status_code}", data={
"time": round((end_time - start_time).microseconds / 1000)
})
else:
return schemas.Response(success=False, message="网络连接失败!")
@router.get("/modulelist", summary="查询已加载的模块ID列表", response_model=schemas.Response)
def modulelist(_: schemas.TokenPayload = Depends(verify_token)):
"""
查询已加载的模块ID列表
"""
module_ids = [module.__name__ for module in ModuleManager().get_modules("test")]
return schemas.Response(success=True, data={
"ids": module_ids
})
@router.get("/moduletest/{moduleid}", summary="模块可用性测试", response_model=schemas.Response)
def moduletest(moduleid: str, _: schemas.TokenPayload = Depends(verify_token)):
"""
模块可用性测试接口
"""
state, errmsg = ModuleManager().test(moduleid)
return schemas.Response(success=state, message=errmsg)
@router.get("/restart", summary="重启系统", response_model=schemas.Response) @router.get("/restart", summary="重启系统", response_model=schemas.Response)
def restart_system(_: schemas.TokenPayload = Depends(verify_token)): def restart_system(_: schemas.TokenPayload = Depends(verify_token)):
""" """

View File

@ -1,4 +1,4 @@
from typing import Generator, Optional from typing import Generator, Optional, Tuple
from app.core.config import settings from app.core.config import settings
from app.helper.module import ModuleHelper from app.helper.module import ModuleHelper
@ -51,6 +51,18 @@ class ModuleManager(metaclass=Singleton):
if hasattr(module, "stop"): if hasattr(module, "stop"):
module.stop() module.stop()
def test(self, modleid: str) -> Tuple[bool, str]:
"""
测试模块
"""
if modleid not in self._running_modules:
return False, "模块未加载,请检查参数设置"
module = self._running_modules[modleid]
if hasattr(module, "test") \
and ObjectUtils.check_method(getattr(module, "test")):
return module.test()
return True, "模块不支持测试"
@staticmethod @staticmethod
def check_setting(setting: Optional[tuple]) -> bool: def check_setting(setting: Optional[tuple]) -> bool:
""" """

View File

@ -35,6 +35,13 @@ class _ModuleBase(metaclass=ABCMeta):
""" """
pass pass
@abstractmethod
def test(self) -> Tuple[bool, str]:
"""
模块测试, 返回测试结果和错误信息
"""
pass
def checkMessage(channel_type: MessageChannel): def checkMessage(channel_type: MessageChannel):
""" """

View File

@ -13,6 +13,7 @@ from app.modules.douban.douban_cache import DoubanCache
from app.modules.douban.scraper import DoubanScraper from app.modules.douban.scraper import DoubanScraper
from app.schemas.types import MediaType from app.schemas.types import MediaType
from app.utils.common import retry from app.utils.common import retry
from app.utils.http import RequestUtils
from app.utils.system import SystemUtils from app.utils.system import SystemUtils
@ -29,6 +30,17 @@ class DoubanModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
ret = RequestUtils().get_res("https://movie.douban.com/")
if ret and ret.status_code == 200:
return True, ""
elif ret:
return False, f"无法连接豆瓣,错误码:{ret.status_code}"
return False, "豆瓣网络连接失败"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
pass pass

View File

@ -17,6 +17,16 @@ class EmbyModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
if self.emby.is_inactive():
self.emby.reconnect()
if self.emby.is_inactive():
return False, "无法连接Emby请检查参数配置"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MEDIASERVER", "emby" return "MEDIASERVER", "emby"

View File

@ -317,6 +317,17 @@ class FanartModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
ret = RequestUtils().get_res("https://webservice.fanart.tv")
if ret and ret.status_code == 200:
return True, ""
elif ret:
return False, f"无法连接fanart错误码{ret.status_code}"
return False, "fanart网络连接失败"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "FANART_API_KEY", True return "FANART_API_KEY", True

View File

@ -27,6 +27,9 @@ class FileTransferModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self):
pass
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
pass pass

View File

@ -132,6 +132,9 @@ class FilterModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self):
pass
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
pass pass

View File

@ -4,6 +4,7 @@ from typing import List, Optional, Tuple, Union
from ruamel.yaml import CommentedMap from ruamel.yaml import CommentedMap
from app.core.context import TorrentInfo from app.core.context import TorrentInfo
from app.helper.sites import SitesHelper
from app.log import logger from app.log import logger
from app.modules import _ModuleBase from app.modules import _ModuleBase
from app.modules.indexer.mtorrent import MTorrentSpider from app.modules.indexer.mtorrent import MTorrentSpider
@ -25,6 +26,15 @@ class IndexerModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
sites = SitesHelper().get_indexers()
if not sites:
return False, "未配置站点或未通过用户认证"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "INDEXER", "builtin" return "INDEXER", "builtin"

View File

@ -28,6 +28,16 @@ class JellyfinModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
if self.jellyfin.is_inactive():
self.jellyfin.reconnect()
if self.jellyfin.is_inactive():
return False, "无法连接Jellyfin请检查参数配置"
return True, ""
def user_authenticate(self, name: str, password: str) -> Optional[str]: def user_authenticate(self, name: str, password: str) -> Optional[str]:
""" """
使用Emby用户辅助完成用户认证 使用Emby用户辅助完成用户认证

View File

@ -17,6 +17,16 @@ class PlexModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
if self.plex.is_inactive():
self.plex.reconnect()
if self.plex.is_inactive():
return False, "无法连接Plex请检查参数配置"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MEDIASERVER", "plex" return "MEDIASERVER", "plex"

View File

@ -26,6 +26,16 @@ class QbittorrentModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
if self.qbittorrent.is_inactive():
self.qbittorrent.reconnect()
if self.qbittorrent.is_inactive():
return False, "无法连接Qbittorrent请检查参数配置"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "DOWNLOADER", "qbittorrent" return "DOWNLOADER", "qbittorrent"

View File

@ -19,6 +19,15 @@ class SlackModule(_ModuleBase):
def stop(self): def stop(self):
self.slack.stop() self.slack.stop()
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
state = self.slack.get_state()
if state:
return True, ""
return False, "Slack未就续请检查参数设置和网络连接"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MESSAGER", "slack" return "MESSAGER", "slack"

View File

@ -87,6 +87,12 @@ class Slack:
except Exception as err: except Exception as err:
logger.error("Slack消息接收服务停止失败: %s" % str(err)) logger.error("Slack消息接收服务停止失败: %s" % str(err))
def get_state(self) -> bool:
"""
获取状态
"""
return True if self._client else False
def send_msg(self, title: str, text: str = "", image: str = "", url: str = "", userid: str = ""): def send_msg(self, title: str, text: str = "", image: str = "", url: str = "", userid: str = ""):
""" """
发送Telegram消息 发送Telegram消息

View File

@ -34,6 +34,9 @@ class SubtitleModule(_ModuleBase):
def stop(self) -> None: def stop(self) -> None:
pass pass
def test(self):
pass
def download_added(self, context: Context, download_dir: Path, torrent_path: Path = None) -> None: def download_added(self, context: Context, download_dir: Path, torrent_path: Path = None) -> None:
""" """
添加下载任务成功后,从站点下载字幕,保存到下载目录 添加下载任务成功后,从站点下载字幕,保存到下载目录

View File

@ -16,6 +16,15 @@ class SynologyChatModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
state = self.synologychat.get_state()
if state:
return True, ""
return False, "SynologyChat未就续请检查参数设置、网络连接以及机器人是否可见"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MESSAGER", "synologychat" return "MESSAGER", "synologychat"

View File

@ -25,6 +25,17 @@ class SynologyChat:
def check_token(self, token: str) -> bool: def check_token(self, token: str) -> bool:
return True if token == self._token else False return True if token == self._token else False
def get_state(self) -> bool:
"""
获取状态
"""
if not self._webhook_url or not self._token:
return False
ret = self.__get_bot_users()
if ret:
return True
return False
def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]: def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]:
""" """
发送Telegram消息 发送Telegram消息

View File

@ -18,6 +18,15 @@ class TelegramModule(_ModuleBase):
def stop(self): def stop(self):
self.telegram.stop() self.telegram.stop()
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
state = self.telegram.get_state()
if state:
return True, ""
return False, "Telegram未就续请检查参数设置和网络连接"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MESSAGER", "telegram" return "MESSAGER", "telegram"

View File

@ -61,6 +61,12 @@ class Telegram:
self._polling_thread.start() self._polling_thread.start()
logger.info("Telegram消息接收服务启动") logger.info("Telegram消息接收服务启动")
def get_state(self) -> bool:
"""
获取状态
"""
return self._bot is not None
def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]: def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]:
""" """
发送Telegram消息 发送Telegram消息

View File

@ -12,6 +12,7 @@ from app.modules.themoviedb.scraper import TmdbScraper
from app.modules.themoviedb.tmdb_cache import TmdbCache from app.modules.themoviedb.tmdb_cache import TmdbCache
from app.modules.themoviedb.tmdbapi import TmdbHelper from app.modules.themoviedb.tmdbapi import TmdbHelper
from app.schemas.types import MediaType, MediaImageType from app.schemas.types import MediaType, MediaImageType
from app.utils.http import RequestUtils
from app.utils.system import SystemUtils from app.utils.system import SystemUtils
@ -38,6 +39,17 @@ class TheMovieDbModule(_ModuleBase):
def stop(self): def stop(self):
self.cache.save() self.cache.save()
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
ret = RequestUtils().get_res(f"https://{settings.TMDB_API_DOMAIN}/3/movie/550?api_key={settings.TMDB_API_KEY}")
if ret and ret.status_code == 200:
return True, ""
elif ret:
return False, f"无法连接 {settings.TMDB_API_DOMAIN},错误码:{ret.status_code}"
return False, f"{settings.TMDB_API_DOMAIN} 网络连接失败"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
pass pass

View File

@ -5,6 +5,7 @@ from app.core.config import settings
from app.log import logger from app.log import logger
from app.modules import _ModuleBase from app.modules import _ModuleBase
from app.modules.thetvdb import tvdbapi from app.modules.thetvdb import tvdbapi
from app.utils.http import RequestUtils
class TheTvDbModule(_ModuleBase): class TheTvDbModule(_ModuleBase):
@ -20,6 +21,17 @@ class TheTvDbModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
ret = RequestUtils().get_res("https://api.thetvdb.com/series/81189")
if ret and ret.status_code == 200:
return True, ""
elif ret:
return False, f"无法连接 api.thetvdb.com错误码{ret.status_code}"
return False, "api.thetvdb.com 网络连接失败"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
pass pass

View File

@ -26,6 +26,16 @@ class TransmissionModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
if self.transmission.is_inactive():
self.transmission.reconnect()
if self.transmission.is_inactive():
return False, "无法连接Transmission请检查参数配置"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "DOWNLOADER", "transmission" return "DOWNLOADER", "transmission"

View File

@ -20,6 +20,15 @@ class WechatModule(_ModuleBase):
def stop(self): def stop(self):
pass pass
def test(self) -> Tuple[bool, str]:
"""
测试模块连接性
"""
state = self.wechat.get_state()
if state:
return True, ""
return False, "获取微信token失败"
def init_setting(self) -> Tuple[str, Union[str, bool]]: def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "MESSAGER", "wechat" return "MESSAGER", "wechat"

View File

@ -47,6 +47,12 @@ class WeChat:
if self._corpid and self._appsecret and self._appid: if self._corpid and self._appsecret and self._appid:
self.__get_access_token() self.__get_access_token()
def get_state(self):
"""
获取状态
"""
return True if self.__get_access_token else False
@retry(Exception, logger=logger) @retry(Exception, logger=logger)
def __get_access_token(self, force=False): def __get_access_token(self, force=False):
""" """