feat 支持Emby/Jellyfin登录认证
This commit is contained in:
parent
47f5942e22
commit
0776a0b235
@ -6,9 +6,7 @@ from fastapi import BackgroundTasks
|
|||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.db.models.user import User
|
from app.core.security import verify_token
|
||||||
from app.db.userauth import get_current_active_superuser
|
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
from app.schemas import MediaType
|
from app.schemas import MediaType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -24,7 +22,7 @@ def start_douban_chain():
|
|||||||
@router.get("/sync", summary="同步豆瓣想看", response_model=schemas.Response)
|
@router.get("/sync", summary="同步豆瓣想看", response_model=schemas.Response)
|
||||||
async def sync_douban(
|
async def sync_douban(
|
||||||
background_tasks: BackgroundTasks,
|
background_tasks: BackgroundTasks,
|
||||||
_: User = Depends(get_current_active_superuser)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
同步豆瓣想看
|
同步豆瓣想看
|
||||||
"""
|
"""
|
||||||
@ -34,7 +32,7 @@ async def sync_douban(
|
|||||||
|
|
||||||
@router.get("/id", summary="豆瓣ID识别", response_model=schemas.Context)
|
@router.get("/id", summary="豆瓣ID识别", response_model=schemas.Context)
|
||||||
async def recognize_doubanid(doubanid: str,
|
async def recognize_doubanid(doubanid: str,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据豆瓣ID识别媒体信息
|
根据豆瓣ID识别媒体信息
|
||||||
"""
|
"""
|
||||||
@ -44,7 +42,7 @@ async def recognize_doubanid(doubanid: str,
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/info", summary="查询豆瓣详情", response_model=schemas.MediaInfo)
|
@router.get("/info", summary="查询豆瓣详情", response_model=schemas.MediaInfo)
|
||||||
async def douban_info(doubanid: str) -> Any:
|
async def douban_info(doubanid: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据豆瓣ID查询豆瓣媒体信息
|
根据豆瓣ID查询豆瓣媒体信息
|
||||||
"""
|
"""
|
||||||
@ -60,7 +58,7 @@ async def douban_movies(sort: str = "R",
|
|||||||
tags: str = "",
|
tags: str = "",
|
||||||
start: int = 0,
|
start: int = 0,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览豆瓣电影信息
|
浏览豆瓣电影信息
|
||||||
"""
|
"""
|
||||||
@ -76,7 +74,7 @@ async def douban_tvs(sort: str = "R",
|
|||||||
tags: str = "",
|
tags: str = "",
|
||||||
start: int = 0,
|
start: int = 0,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览豆瓣剧集信息
|
浏览豆瓣剧集信息
|
||||||
"""
|
"""
|
||||||
@ -90,7 +88,7 @@ async def douban_tvs(sort: str = "R",
|
|||||||
@router.get("/movie_top250", summary="豆瓣电影TOP250", response_model=List[schemas.MediaInfo])
|
@router.get("/movie_top250", summary="豆瓣电影TOP250", response_model=List[schemas.MediaInfo])
|
||||||
async def movie_top250(page: int = 1,
|
async def movie_top250(page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览豆瓣剧集信息
|
浏览豆瓣剧集信息
|
||||||
"""
|
"""
|
||||||
@ -101,7 +99,7 @@ async def movie_top250(page: int = 1,
|
|||||||
@router.get("/tv_weekly_chinese", summary="豆瓣国产剧集周榜", response_model=List[schemas.MediaInfo])
|
@router.get("/tv_weekly_chinese", summary="豆瓣国产剧集周榜", response_model=List[schemas.MediaInfo])
|
||||||
async def tv_weekly_chinese(page: int = 1,
|
async def tv_weekly_chinese(page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
中国每周剧集口碑榜
|
中国每周剧集口碑榜
|
||||||
"""
|
"""
|
||||||
@ -112,7 +110,7 @@ async def tv_weekly_chinese(page: int = 1,
|
|||||||
@router.get("/tv_weekly_global", summary="豆瓣全球剧集周榜", response_model=List[schemas.MediaInfo])
|
@router.get("/tv_weekly_global", summary="豆瓣全球剧集周榜", response_model=List[schemas.MediaInfo])
|
||||||
async def tv_weekly_global(page: int = 1,
|
async def tv_weekly_global(page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
全球每周剧集口碑榜
|
全球每周剧集口碑榜
|
||||||
"""
|
"""
|
||||||
|
@ -4,11 +4,10 @@ from fastapi import APIRouter, Depends
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
|
from app.core.security import verify_token
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.db.models.downloadhistory import DownloadHistory
|
from app.db.models.downloadhistory import DownloadHistory
|
||||||
from app.db.models.transferhistory import TransferHistory
|
from app.db.models.transferhistory import TransferHistory
|
||||||
from app.db.models.user import User
|
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ router = APIRouter()
|
|||||||
async def download_history(page: int = 1,
|
async def download_history(page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询下载历史记录
|
查询下载历史记录
|
||||||
"""
|
"""
|
||||||
@ -29,7 +28,7 @@ async def transfer_history(title: str = None,
|
|||||||
page: int = 1,
|
page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询转移历史记录
|
查询转移历史记录
|
||||||
"""
|
"""
|
||||||
|
@ -6,10 +6,12 @@ from fastapi.security import OAuth2PasswordRequestForm
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
|
from app.chain.user import UserChain
|
||||||
from app.core import security
|
from app.core import security
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.db.models.user import User
|
from app.db.models.user import User
|
||||||
|
from app.log import logger
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -21,13 +23,21 @@ async def login_access_token(
|
|||||||
"""
|
"""
|
||||||
获取认证Token
|
获取认证Token
|
||||||
"""
|
"""
|
||||||
|
# 检查数据库
|
||||||
user = User.authenticate(
|
user = User.authenticate(
|
||||||
db=db,
|
db=db,
|
||||||
name=form_data.username,
|
name=form_data.username,
|
||||||
password=form_data.password
|
password=form_data.password
|
||||||
)
|
)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=400, detail="用户名或密码不正确")
|
# 请求协助认证
|
||||||
|
logger.warn("登录用户本地不匹配,尝试辅助认证 ...")
|
||||||
|
token = UserChain().user_authenticate(form_data.username, form_data.password)
|
||||||
|
if not token:
|
||||||
|
raise HTTPException(status_code=400, detail="用户名或密码不正确")
|
||||||
|
else:
|
||||||
|
logger.info(f"辅助认证成功,用户信息: {token}")
|
||||||
|
user = schemas.User(id=-1, name=form_data.username, is_active=True, is_superuser=False)
|
||||||
elif not user.is_active:
|
elif not user.is_active:
|
||||||
raise HTTPException(status_code=400, detail="用户未启用")
|
raise HTTPException(status_code=400, detail="用户未启用")
|
||||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
@ -4,8 +4,7 @@ from fastapi import APIRouter, Depends
|
|||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.media import MediaChain
|
from app.chain.media import MediaChain
|
||||||
from app.db.models.user import User
|
from app.core.security import verify_token
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ router = APIRouter()
|
|||||||
@router.get("/recognize", summary="识别媒体信息", response_model=schemas.Context)
|
@router.get("/recognize", summary="识别媒体信息", response_model=schemas.Context)
|
||||||
async def recognize(title: str,
|
async def recognize(title: str,
|
||||||
subtitle: str = None,
|
subtitle: str = None,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据标题、副标题识别媒体信息
|
根据标题、副标题识别媒体信息
|
||||||
"""
|
"""
|
||||||
@ -24,7 +23,7 @@ async def recognize(title: str,
|
|||||||
|
|
||||||
@router.get("/search", summary="搜索媒体信息", response_model=List[schemas.MediaInfo])
|
@router.get("/search", summary="搜索媒体信息", response_model=List[schemas.MediaInfo])
|
||||||
async def search_by_title(title: str,
|
async def search_by_title(title: str,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
模糊搜索媒体信息列表
|
模糊搜索媒体信息列表
|
||||||
"""
|
"""
|
||||||
|
@ -4,16 +4,15 @@ from fastapi import APIRouter, Depends
|
|||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.plugin import PluginManager
|
from app.core.plugin import PluginManager
|
||||||
from app.db.models.user import User
|
from app.core.security import verify_token
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", summary="所有插件", response_model=List[schemas.Plugin])
|
@router.get("/", summary="所有插件", response_model=List[schemas.Plugin])
|
||||||
async def all_plugins(_: User = Depends(get_current_active_user)) -> Any:
|
async def all_plugins(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询所有插件清单
|
查询所有插件清单
|
||||||
"""
|
"""
|
||||||
@ -21,7 +20,7 @@ async def all_plugins(_: User = Depends(get_current_active_user)) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/installed", summary="已安装插件", response_model=List[str])
|
@router.get("/installed", summary="已安装插件", response_model=List[str])
|
||||||
async def installed_plugins(_: User = Depends(get_current_active_user)) -> Any:
|
async def installed_plugins(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询用户已安装插件清单
|
查询用户已安装插件清单
|
||||||
"""
|
"""
|
||||||
@ -29,7 +28,7 @@ async def installed_plugins(_: User = Depends(get_current_active_user)) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/{plugin_id}", summary="获取插件配置")
|
@router.get("/{plugin_id}", summary="获取插件配置")
|
||||||
async def plugin_config(plugin_id: str, _: User = Depends(get_current_active_user)) -> dict:
|
async def plugin_config(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> dict:
|
||||||
"""
|
"""
|
||||||
根据插件ID获取插件配置信息
|
根据插件ID获取插件配置信息
|
||||||
"""
|
"""
|
||||||
@ -38,7 +37,7 @@ async def plugin_config(plugin_id: str, _: User = Depends(get_current_active_use
|
|||||||
|
|
||||||
@router.put("/{plugin_id}", summary="更新插件配置", response_model=schemas.Response)
|
@router.put("/{plugin_id}", summary="更新插件配置", response_model=schemas.Response)
|
||||||
async def set_plugin_config(plugin_id: str, conf: dict,
|
async def set_plugin_config(plugin_id: str, conf: dict,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据插件ID获取插件配置信息
|
根据插件ID获取插件配置信息
|
||||||
"""
|
"""
|
||||||
@ -48,7 +47,7 @@ async def set_plugin_config(plugin_id: str, conf: dict,
|
|||||||
|
|
||||||
@router.post("/{plugin_id}/install", summary="安装插件", response_model=schemas.Response)
|
@router.post("/{plugin_id}/install", summary="安装插件", response_model=schemas.Response)
|
||||||
async def install_plugin(plugin_id: str,
|
async def install_plugin(plugin_id: str,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
安装插件
|
安装插件
|
||||||
"""
|
"""
|
||||||
@ -64,7 +63,7 @@ async def install_plugin(plugin_id: str,
|
|||||||
|
|
||||||
|
|
||||||
@router.delete("/{plugin_id}", summary="卸载插件", response_model=schemas.Response)
|
@router.delete("/{plugin_id}", summary="卸载插件", response_model=schemas.Response)
|
||||||
async def uninstall_plugin(plugin_id: str, _: User = Depends(get_current_active_user)) -> Any:
|
async def uninstall_plugin(plugin_id: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
卸载插件
|
卸载插件
|
||||||
"""
|
"""
|
||||||
|
@ -4,8 +4,7 @@ from fastapi import APIRouter, Depends
|
|||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.search import SearchChain
|
from app.chain.search import SearchChain
|
||||||
from app.db.models.user import User
|
from app.core.security import verify_token
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -14,7 +13,7 @@ router = APIRouter()
|
|||||||
@router.get("/tmdbid", summary="精确搜索资源", response_model=List[schemas.Context])
|
@router.get("/tmdbid", summary="精确搜索资源", response_model=List[schemas.Context])
|
||||||
async def search_by_tmdbid(tmdbid: int,
|
async def search_by_tmdbid(tmdbid: int,
|
||||||
mtype: str = None,
|
mtype: str = None,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据TMDBID精确搜索站点资源
|
根据TMDBID精确搜索站点资源
|
||||||
"""
|
"""
|
||||||
@ -26,7 +25,7 @@ async def search_by_tmdbid(tmdbid: int,
|
|||||||
|
|
||||||
@router.get("/title", summary="模糊搜索资源", response_model=List[schemas.TorrentInfo])
|
@router.get("/title", summary="模糊搜索资源", response_model=List[schemas.TorrentInfo])
|
||||||
async def search_by_title(title: str,
|
async def search_by_title(title: str,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据名称模糊搜索站点资源
|
根据名称模糊搜索站点资源
|
||||||
"""
|
"""
|
||||||
|
@ -6,18 +6,17 @@ from sqlalchemy.orm import Session
|
|||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.cookiecloud import CookieCloudChain
|
from app.chain.cookiecloud import CookieCloudChain
|
||||||
from app.chain.site import SiteChain
|
from app.chain.site import SiteChain
|
||||||
|
from app.core.security import verify_token
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.db.models.site import Site
|
from app.db.models.site import Site
|
||||||
from app.db.models.user import User
|
|
||||||
from app.db.siteicon_oper import SiteIconOper
|
from app.db.siteicon_oper import SiteIconOper
|
||||||
from app.db.userauth import get_current_active_user, get_current_active_superuser
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", summary="所有站点", response_model=List[schemas.Site])
|
@router.get("/", summary="所有站点", response_model=List[schemas.Site])
|
||||||
async def read_sites(db: Session = Depends(get_db),
|
async def read_sites(db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_user)) -> List[dict]:
|
_: schemas.TokenPayload = Depends(verify_token)) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
获取站点列表
|
获取站点列表
|
||||||
"""
|
"""
|
||||||
@ -29,7 +28,7 @@ async def update_site(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
site_in: schemas.Site,
|
site_in: schemas.Site,
|
||||||
_: User = Depends(get_current_active_superuser),
|
_: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
更新站点信息
|
更新站点信息
|
||||||
@ -48,7 +47,7 @@ async def update_site(
|
|||||||
async def read_site(
|
async def read_site(
|
||||||
site_id: int,
|
site_id: int,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_user),
|
_: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
获取站点信息
|
获取站点信息
|
||||||
@ -63,7 +62,7 @@ async def read_site(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/cookiecloud", summary="CookieCloud同步", response_model=schemas.Response)
|
@router.get("/cookiecloud", summary="CookieCloud同步", response_model=schemas.Response)
|
||||||
async def cookie_cloud_sync(_: User = Depends(get_current_active_user)) -> Any:
|
async def cookie_cloud_sync(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
运行CookieCloud同步站点信息
|
运行CookieCloud同步站点信息
|
||||||
"""
|
"""
|
||||||
@ -79,7 +78,7 @@ async def update_cookie(
|
|||||||
username: str,
|
username: str,
|
||||||
password: str,
|
password: str,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
使用用户密码更新站点Cookie
|
使用用户密码更新站点Cookie
|
||||||
"""
|
"""
|
||||||
@ -101,7 +100,7 @@ async def update_cookie(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/test", summary="连接测试", response_model=schemas.Response)
|
@router.get("/test", summary="连接测试", response_model=schemas.Response)
|
||||||
async def test_site(domain: str, _: User = Depends(get_current_active_user)) -> Any:
|
async def test_site(domain: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
测试站点是否可用
|
测试站点是否可用
|
||||||
"""
|
"""
|
||||||
@ -110,7 +109,7 @@ async def test_site(domain: str, _: User = Depends(get_current_active_user)) ->
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/icon", summary="站点图标", response_model=schemas.Response)
|
@router.get("/icon", summary="站点图标", response_model=schemas.Response)
|
||||||
async def site_icon(domain: str, _: User = Depends(get_current_active_user)) -> Any:
|
async def site_icon(domain: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
获取站点图标:base64或者url
|
获取站点图标:base64或者url
|
||||||
"""
|
"""
|
||||||
|
@ -6,10 +6,9 @@ from sqlalchemy.orm import Session
|
|||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
from app.core.security import verify_token
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.db.models.subscribe import Subscribe
|
from app.db.models.subscribe import Subscribe
|
||||||
from app.db.models.user import User
|
|
||||||
from app.db.userauth import get_current_active_superuser
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -27,7 +26,7 @@ def start_subscribe_chain(title: str, year: str,
|
|||||||
@router.get("/", summary="所有订阅", response_model=List[schemas.Subscribe])
|
@router.get("/", summary="所有订阅", response_model=List[schemas.Subscribe])
|
||||||
async def read_subscribes(
|
async def read_subscribes(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_superuser)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询所有订阅
|
查询所有订阅
|
||||||
"""
|
"""
|
||||||
@ -38,7 +37,7 @@ async def read_subscribes(
|
|||||||
async def create_subscribe(
|
async def create_subscribe(
|
||||||
*,
|
*,
|
||||||
subscribe_in: schemas.Subscribe,
|
subscribe_in: schemas.Subscribe,
|
||||||
_: User = Depends(get_current_active_superuser),
|
_: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
新增订阅
|
新增订阅
|
||||||
@ -52,7 +51,7 @@ async def update_subscribe(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
subscribe_in: schemas.Subscribe,
|
subscribe_in: schemas.Subscribe,
|
||||||
_: User = Depends(get_current_active_superuser),
|
_: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
更新订阅信息
|
更新订阅信息
|
||||||
@ -72,7 +71,7 @@ async def delete_subscribe(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
subscribe_in: schemas.Subscribe,
|
subscribe_in: schemas.Subscribe,
|
||||||
_: User = Depends(get_current_active_superuser),
|
_: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
删除订阅信息
|
删除订阅信息
|
||||||
@ -136,7 +135,7 @@ async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks,
|
|||||||
|
|
||||||
@router.get("/refresh", summary="刷新订阅", response_model=schemas.Response)
|
@router.get("/refresh", summary="刷新订阅", response_model=schemas.Response)
|
||||||
async def refresh_subscribes(
|
async def refresh_subscribes(
|
||||||
_: User = Depends(get_current_active_superuser)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
刷新所有订阅
|
刷新所有订阅
|
||||||
"""
|
"""
|
||||||
@ -146,7 +145,7 @@ async def refresh_subscribes(
|
|||||||
|
|
||||||
@router.get("/search", summary="搜索订阅", response_model=schemas.Response)
|
@router.get("/search", summary="搜索订阅", response_model=schemas.Response)
|
||||||
async def search_subscribes(
|
async def search_subscribes(
|
||||||
_: User = Depends(get_current_active_superuser)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
搜索所有订阅
|
搜索所有订阅
|
||||||
"""
|
"""
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, Depends
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
from app import schemas
|
||||||
|
from app.core.security import verify_token
|
||||||
from app.helper.progress import ProgressHelper
|
from app.helper.progress import ProgressHelper
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/progress/{process_type}", summary="实时进度")
|
@router.get("/progress/{process_type}", summary="实时进度")
|
||||||
async def get_progress(process_type: str):
|
async def get_progress(process_type: str, _: schemas.TokenPayload = Depends(verify_token)):
|
||||||
"""
|
"""
|
||||||
实时获取处理进度,返回格式为SSE
|
实时获取处理进度,返回格式为SSE
|
||||||
"""
|
"""
|
||||||
|
@ -5,8 +5,7 @@ from fastapi import APIRouter, Depends
|
|||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.db.models.user import User
|
from app.core.security import verify_token
|
||||||
from app.db.userauth import get_current_active_user
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -30,7 +29,7 @@ async def tmdb_movies(sort_by: str = "popularity.desc",
|
|||||||
with_genres: str = "",
|
with_genres: str = "",
|
||||||
with_original_language: str = "",
|
with_original_language: str = "",
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览TMDB电影信息
|
浏览TMDB电影信息
|
||||||
"""
|
"""
|
||||||
@ -49,7 +48,7 @@ async def tmdb_tvs(sort_by: str = "popularity.desc",
|
|||||||
with_genres: str = "",
|
with_genres: str = "",
|
||||||
with_original_language: str = "",
|
with_original_language: str = "",
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览TMDB剧集信息
|
浏览TMDB剧集信息
|
||||||
"""
|
"""
|
||||||
@ -65,7 +64,7 @@ async def tmdb_tvs(sort_by: str = "popularity.desc",
|
|||||||
|
|
||||||
@router.get("/trending", summary="TMDB流行趋势", response_model=List[schemas.MediaInfo])
|
@router.get("/trending", summary="TMDB流行趋势", response_model=List[schemas.MediaInfo])
|
||||||
async def tmdb_trending(page: int = 1,
|
async def tmdb_trending(page: int = 1,
|
||||||
_: User = Depends(get_current_active_user)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
浏览TMDB剧集信息
|
浏览TMDB剧集信息
|
||||||
"""
|
"""
|
||||||
|
@ -5,11 +5,11 @@ from app.chain import ChainBase
|
|||||||
|
|
||||||
class UserChain(ChainBase):
|
class UserChain(ChainBase):
|
||||||
|
|
||||||
def user_authenticate(self, name, password) -> Optional[bool]:
|
def user_authenticate(self, name, password) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
辅助完成用户认证
|
辅助完成用户认证
|
||||||
:param name: 用户名
|
:param name: 用户名
|
||||||
:param password: 密码
|
:param password: 密码
|
||||||
:return: bool
|
:return: token
|
||||||
"""
|
"""
|
||||||
return self.run_module("user_authenticate", name=name, password=password)
|
return self.run_module("user_authenticate", name=name, password=password)
|
||||||
|
@ -8,8 +8,11 @@ from typing import Any, Union, Optional
|
|||||||
import jwt
|
import jwt
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util.Padding import pad
|
from Crypto.Util.Padding import pad
|
||||||
|
from fastapi import HTTPException, status, Depends
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
|
|
||||||
|
from app import schemas
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
@ -36,6 +39,19 @@ def create_access_token(
|
|||||||
return encoded_jwt
|
return encoded_jwt
|
||||||
|
|
||||||
|
|
||||||
|
def verify_token(token: str = Depends(reusable_oauth2)) -> schemas.TokenPayload:
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(
|
||||||
|
token, settings.SECRET_KEY, algorithms=[ALGORITHM]
|
||||||
|
)
|
||||||
|
return schemas.TokenPayload(**payload)
|
||||||
|
except (jwt.DecodeError, jwt.InvalidTokenError, jwt.ImmatureSignatureError):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="token校验不通过",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||||
return pwd_context.verify(plain_password, hashed_password)
|
return pwd_context.verify(plain_password, hashed_password)
|
||||||
|
|
||||||
|
@ -1,28 +1,16 @@
|
|||||||
import jwt
|
from fastapi import Depends, HTTPException
|
||||||
from fastapi import Depends, HTTPException, status
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import settings
|
from app.core.security import verify_token
|
||||||
from app.core import security
|
|
||||||
from app.core.security import reusable_oauth2
|
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.db.models.user import User
|
from app.db.models.user import User
|
||||||
|
|
||||||
|
|
||||||
def get_current_user(
|
def get_current_user(
|
||||||
db: Session = Depends(get_db), token: str = Depends(reusable_oauth2)
|
db: Session = Depends(get_db),
|
||||||
|
token_data: schemas.TokenPayload = Depends(verify_token)
|
||||||
) -> User:
|
) -> User:
|
||||||
try:
|
|
||||||
payload = jwt.decode(
|
|
||||||
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
|
|
||||||
)
|
|
||||||
token_data = schemas.TokenPayload(**payload)
|
|
||||||
except (jwt.DecodeError, jwt.InvalidTokenError, jwt.ImmatureSignatureError):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
|
||||||
detail="token校验不通过",
|
|
||||||
)
|
|
||||||
user = User.get(db, rid=token_data.sub)
|
user = User.get(db, rid=token_data.sub)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
@ -21,13 +21,14 @@ class EmbyModule(_ModuleBase):
|
|||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MEDIASERVER", "emby"
|
return "MEDIASERVER", "emby"
|
||||||
|
|
||||||
def user_authenticate(self, name, password) -> Optional[bool]:
|
def user_authenticate(self, name: str, password: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
使用Emby用户辅助完成用户认证
|
使用Emby用户辅助完成用户认证
|
||||||
:param name: 用户名
|
:param name: 用户名
|
||||||
:param password: 密码
|
:param password: 密码
|
||||||
:return: bool
|
:return: token or None
|
||||||
"""
|
"""
|
||||||
|
# Emby认证
|
||||||
return self.emby.authenticate(name, password)
|
return self.emby.authenticate(name, password)
|
||||||
|
|
||||||
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
||||||
|
@ -6,10 +6,10 @@ from typing import List, Optional, Union, Dict
|
|||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import RefreshMediaItem
|
from app.schemas import RefreshMediaItem
|
||||||
|
from app.schemas.types import MediaType
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
from app.schemas.types import MediaType
|
|
||||||
|
|
||||||
|
|
||||||
class Emby(metaclass=Singleton):
|
class Emby(metaclass=Singleton):
|
||||||
@ -87,27 +87,37 @@ class Emby(metaclass=Singleton):
|
|||||||
logger.error(f"连接Users出错:" + str(e))
|
logger.error(f"连接Users出错:" + str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def authenticate(self, username: str, password: str) -> Optional[bool]:
|
def authenticate(self, username: str, password: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
用户认证
|
用户认证
|
||||||
|
:param username: 用户名
|
||||||
|
:param password: 密码
|
||||||
|
:return: 认证token
|
||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
req_url = "%semby/Users/AuthenticateByName" % self._host
|
req_url = "%semby/Users/AuthenticateByName" % self._host
|
||||||
try:
|
try:
|
||||||
res = RequestUtils(content_type="application/json").post_res(
|
res = RequestUtils(headers={
|
||||||
|
'X-Emby-Authorization': f'MediaBrowser Client="MoviePilot", '
|
||||||
|
f'Device="Axios", '
|
||||||
|
f'DeviceId="1", '
|
||||||
|
f'Version="10.8.0", '
|
||||||
|
f'Token="{self._apikey}"',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
"Accept": "application/json"
|
||||||
|
}).post_res(
|
||||||
url=req_url,
|
url=req_url,
|
||||||
data=json.dumps({
|
data=json.dumps({
|
||||||
"Username": username,
|
"Username": username,
|
||||||
"Pw": password,
|
"Pw": password
|
||||||
"KeepMeLoggedIn": False
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
if res:
|
if res:
|
||||||
auth_token = res.json().get("AccessToken")
|
auth_token = res.json().get("AccessToken")
|
||||||
if auth_token:
|
if auth_token:
|
||||||
logger.info(f"用户 {username} Emby认证成功")
|
logger.info(f"用户 {username} Emby认证成功")
|
||||||
return True
|
return auth_token
|
||||||
else:
|
else:
|
||||||
logger.error(f"Users/AuthenticateByName 未获取到返回数据")
|
logger.error(f"Users/AuthenticateByName 未获取到返回数据")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -22,13 +22,14 @@ class JellyfinModule(_ModuleBase):
|
|||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MEDIASERVER", "jellyfin"
|
return "MEDIASERVER", "jellyfin"
|
||||||
|
|
||||||
def user_authenticate(self, name, password) -> Optional[bool]:
|
def user_authenticate(self, name: str, password: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
使用Emby用户辅助完成用户认证
|
使用Emby用户辅助完成用户认证
|
||||||
:param name: 用户名
|
:param name: 用户名
|
||||||
:param password: 密码
|
:param password: 密码
|
||||||
:return: bool
|
:return: Token or None
|
||||||
"""
|
"""
|
||||||
|
# Jellyfin认证
|
||||||
return self.jellyfin.authenticate(name, password)
|
return self.jellyfin.authenticate(name, password)
|
||||||
|
|
||||||
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
||||||
|
@ -84,15 +84,26 @@ class Jellyfin(metaclass=Singleton):
|
|||||||
logger.error(f"连接Users出错:" + str(e))
|
logger.error(f"连接Users出错:" + str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def authenticate(self, username: str, password: str) -> Optional[bool]:
|
def authenticate(self, username: str, password: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
用户认证
|
用户认证
|
||||||
|
:param username: 用户名
|
||||||
|
:param password: 密码
|
||||||
|
:return: 认证成功返回token,否则返回None
|
||||||
"""
|
"""
|
||||||
if not self._host or not self._apikey:
|
if not self._host or not self._apikey:
|
||||||
return None
|
return None
|
||||||
req_url = "%sUsers/authenticatebyname" % self._host
|
req_url = "%sUsers/authenticatebyname" % self._host
|
||||||
try:
|
try:
|
||||||
res = RequestUtils(content_type="application/json").post_res(
|
res = RequestUtils(headers={
|
||||||
|
'X-Emby-Authorization': f'MediaBrowser Client="MoviePilot", '
|
||||||
|
f'Device="Axios", '
|
||||||
|
f'DeviceId="1", '
|
||||||
|
f'Version="10.8.0", '
|
||||||
|
f'Token="{self._apikey}"',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
"Accept": "application/json"
|
||||||
|
}).post_res(
|
||||||
url=req_url,
|
url=req_url,
|
||||||
data=json.dumps({
|
data=json.dumps({
|
||||||
"Username": username,
|
"Username": username,
|
||||||
@ -103,7 +114,7 @@ class Jellyfin(metaclass=Singleton):
|
|||||||
auth_token = res.json().get("AccessToken")
|
auth_token = res.json().get("AccessToken")
|
||||||
if auth_token:
|
if auth_token:
|
||||||
logger.info(f"用户 {username} Jellyfin认证成功")
|
logger.info(f"用户 {username} Jellyfin认证成功")
|
||||||
return True
|
return auth_token
|
||||||
else:
|
else:
|
||||||
logger.error(f"Users/AuthenticateByName 未获取到返回数据")
|
logger.error(f"Users/AuthenticateByName 未获取到返回数据")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user