This commit is contained in:
jxxghp
2023-06-06 07:15:17 +08:00
commit 4d06f86e62
217 changed files with 13959 additions and 0 deletions

0
app/api/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

12
app/api/apiv1.py Normal file
View File

@ -0,0 +1,12 @@
from fastapi import APIRouter
from app.api.endpoints import login, users, sites, messages, webhooks, subscribes, media
api_router = APIRouter()
api_router.include_router(login.router, tags=["login"])
api_router.include_router(users.router, prefix="/users", tags=["users"])
api_router.include_router(sites.router, prefix="/sites", tags=["sites"])
api_router.include_router(messages.router, prefix="/messages", tags=["messages"])
api_router.include_router(webhooks.router, prefix="/webhooks", tags=["webhooks"])
api_router.include_router(subscribes.router, prefix="/subscribes", tags=["subscribes"])
api_router.include_router(media.router, prefix="/media", tags=["media"])

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,38 @@
from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app import schemas
from app.core import security, settings
from app.db import get_db
from app.db.models.user import User
router = APIRouter()
@router.post("/login/access-token", response_model=schemas.Token)
async def login_access_token(
db: Session = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()
) -> Any:
"""
获取认证Token
"""
user = User.authenticate(
db=db,
email=form_data.username,
password=form_data.password
)
if not user:
raise HTTPException(status_code=400, detail="用户名或密码不正确")
elif not user.is_active:
raise HTTPException(status_code=400, detail="用户未启用")
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
return {
"access_token": security.create_access_token(
user.id, expires_delta=access_token_expires
),
"token_type": "bearer",
}

View File

@ -0,0 +1,25 @@
from fastapi import APIRouter, HTTPException, Depends
from app import schemas
from app.chain.identify import IdentifyChain
from app.db.models.user import User
from app.db.userauth import get_current_active_user
router = APIRouter()
@router.post("/recognize", response_model=schemas.Context)
async def recognize(title: str,
subtitle: str = None,
current_user: User = Depends(get_current_active_user)):
"""
识别媒体信息
"""
if not current_user:
raise HTTPException(
status_code=400,
detail="需要授权",
)
# 识别媒体信息
context = IdentifyChain().process(title=title, subtitle=subtitle)
return context.to_dict()

View File

@ -0,0 +1,51 @@
from typing import Union
from fastapi import APIRouter, BackgroundTasks
from fastapi import Request
from app import schemas
from app.chain.user_message import UserMessageChain
from app.core import settings
from app.log import logger
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
router = APIRouter()
def start_message_chain(request: Request):
"""
启动链式任务
"""
UserMessageChain().process(request)
@router.post("/", response_model=schemas.Response)
async def user_message(background_tasks: BackgroundTasks, request: Request):
"""
用户消息响应
"""
background_tasks.add_task(start_message_chain, request)
return {"success": True}
@router.get("/")
async def wechat_verify(echostr: str, msg_signature: str, timestamp: Union[str, int], nonce: str):
"""
用户消息响应
"""
logger.info(f"收到微信验证请求: {echostr}")
try:
wxcpt = WXBizMsgCrypt(sToken=settings.WECHAT_TOKEN,
sEncodingAESKey=settings.WECHAT_ENCODING_AESKEY,
sReceiveId=settings.WECHAT_CORPID)
except Exception as err:
logger.error(f"微信请求验证失败: {err}")
return str(err)
ret, sEchoStr = wxcpt.VerifyURL(sMsgSignature=msg_signature,
sTimeStamp=timestamp,
sNonce=nonce,
sEchoStr=echostr)
if ret != 0:
logger.error("微信请求验证失败 VerifyURL ret: %s" % str(ret))
# 验证URL成功将sEchoStr返回给企业号
return sEchoStr

View File

@ -0,0 +1,43 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app import schemas
from app.chain.cookiecloud import CookieCloudChain
from app.db import get_db
from app.db.models.site import Site
from app.db.models.user import User
from app.db.userauth import get_current_active_user
router = APIRouter()
@router.get("/", response_model=List[schemas.Site])
async def read_sites(db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)) -> List[dict]:
"""
获取站点列表
"""
if not current_user:
raise HTTPException(
status_code=400,
detail="需要授权",
)
return Site.list(db)
@router.get("/cookiecloud", response_model=schemas.Response)
async def cookie_cloud_sync(current_user: User = Depends(get_current_active_user)) -> dict:
"""
运行CookieCloud同步站点信息
"""
if not current_user:
raise HTTPException(
status_code=400,
detail="需要授权",
)
status, error_msg = CookieCloudChain().process()
if not status:
return {"success": False, "message": error_msg}
return {"success": True, "message": error_msg}

View File

@ -0,0 +1,90 @@
from typing import List
from fastapi import APIRouter, Request, BackgroundTasks, Depends, HTTPException, Header
from sqlalchemy.orm import Session
from app import schemas
from app.chain.subscribe import SubscribeChain
from app.core import settings
from app.db import get_db
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.utils.types import MediaType
router = APIRouter()
def start_subscribe_chain(title: str,
mtype: MediaType, tmdbid: str, season: int, username: str):
"""
启动订阅链式任务
"""
SubscribeChain().process(title=title,
mtype=mtype, tmdbid=tmdbid, season=season, username=username)
@router.get("/", response_model=List[schemas.Subscribe])
async def read_subscribes(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_superuser)):
"""
查询所有订阅
"""
if not current_user:
raise HTTPException(
status_code=400,
detail="需要授权",
)
return Subscribe.list(db)
@router.post("/seerr", response_model=schemas.Response)
async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks,
authorization: str = Header(None)):
"""
Jellyseerr/Overseerr订阅
"""
if not authorization or authorization != settings.API_TOKEN:
raise HTTPException(
status_code=400,
detail="授权失败",
)
req_json = await request.json()
if not req_json:
raise HTTPException(
status_code=500,
detail="报文内容为空",
)
notification_type = req_json.get("notification_type")
if notification_type not in ["MEDIA_APPROVED", "MEDIA_AUTO_APPROVED"]:
return {"success": False, "message": "不支持的通知类型"}
subject = req_json.get("subject")
media_type = MediaType.MOVIE if req_json.get("media", {}).get("media_type") == "movie" else MediaType.TV
tmdbId = req_json.get("media", {}).get("tmdbId")
if not media_type or not tmdbId or not subject:
return {"success": False, "message": "请求参数不正确"}
user_name = req_json.get("request", {}).get("requestedBy_username")
# 添加订阅
if media_type == MediaType.MOVIE:
background_tasks.add_task(start_subscribe_chain,
mtype=media_type,
tmdbid=tmdbId,
title=subject,
season=0,
username=user_name)
else:
seasons = []
for extra in req_json.get("extra", []):
if extra.get("name") == "Requested Seasons":
seasons = [int(str(sea).strip()) for sea in extra.get("value").split(", ") if str(sea).isdigit()]
break
for season in seasons:
background_tasks.add_task(start_subscribe_chain,
mtype=media_type,
tmdbid=tmdbId,
title=subject,
season=season,
username=user_name)
return {"success": True}

View File

@ -0,0 +1,74 @@
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app import schemas
from app.core.security import get_password_hash
from app.db import get_db
from app.db.models.user import User
from app.db.userauth import get_current_active_superuser, get_current_active_user
router = APIRouter()
@router.get("/", response_model=List[schemas.User])
async def read_users(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_superuser),
) -> Any:
"""
查询用户列表
"""
users = current_user.list(db)
return users
@router.post("/", response_model=schemas.User)
async def create_user(
*,
db: Session = Depends(get_db),
user_in: schemas.UserCreate,
current_user: User = Depends(get_current_active_superuser),
) -> Any:
"""
新增用户
"""
user = current_user.get_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="用户已存在",
)
user_info = user_in.dict()
if user_info.get("password"):
user_info["hashed_password"] = get_password_hash(user_info["password"])
user_info.pop("password")
user = User(**user_info)
user = user.create(db)
return user
@router.get("/{user_id}", response_model=schemas.User)
async def read_user_by_id(
user_id: int,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
查询用户详情
"""
user = current_user.get(db, rid=user_id)
if not user:
raise HTTPException(
status_code=404,
detail="用户不存在",
)
if user == current_user:
return user
if not user.is_superuser:
raise HTTPException(
status_code=400,
detail="用户权限不足"
)
return user

View File

@ -0,0 +1,27 @@
from fastapi import APIRouter, BackgroundTasks, Request
from app import schemas
from app.chain.webhook_message import WebhookMessageChain
from app.core import settings
router = APIRouter()
def start_webhook_chain(message: dict):
"""
启动链式任务
"""
WebhookMessageChain().process(message)
@router.post("/", response_model=schemas.Response)
async def webhook_message(background_tasks: BackgroundTasks, token: str, request: Request):
"""
Webhook响应
"""
if token != settings.API_TOKEN:
return {"success": False, "message": "token认证不通过"}
background_tasks.add_task(start_webhook_chain, await request.json())
return {"success": True}