init
This commit is contained in:
0
app/api/__init__.py
Normal file
0
app/api/__init__.py
Normal file
BIN
app/api/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/api/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/__pycache__/apiv1.cpython-310.pyc
Normal file
BIN
app/api/__pycache__/apiv1.cpython-310.pyc
Normal file
Binary file not shown.
12
app/api/apiv1.py
Normal file
12
app/api/apiv1.py
Normal 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"])
|
0
app/api/endpoints/__init__.py
Normal file
0
app/api/endpoints/__init__.py
Normal file
BIN
app/api/endpoints/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/login.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/login.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/media.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/media.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/messages.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/messages.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/sites.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/sites.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/subscribes.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/subscribes.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/users.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/users.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/api/endpoints/__pycache__/webhooks.cpython-310.pyc
Normal file
BIN
app/api/endpoints/__pycache__/webhooks.cpython-310.pyc
Normal file
Binary file not shown.
38
app/api/endpoints/login.py
Normal file
38
app/api/endpoints/login.py
Normal 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",
|
||||
}
|
25
app/api/endpoints/media.py
Normal file
25
app/api/endpoints/media.py
Normal 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()
|
51
app/api/endpoints/messages.py
Normal file
51
app/api/endpoints/messages.py
Normal 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
|
43
app/api/endpoints/sites.py
Normal file
43
app/api/endpoints/sites.py
Normal 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}
|
90
app/api/endpoints/subscribes.py
Normal file
90
app/api/endpoints/subscribes.py
Normal 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}
|
74
app/api/endpoints/users.py
Normal file
74
app/api/endpoints/users.py
Normal 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
|
27
app/api/endpoints/webhooks.py
Normal file
27
app/api/endpoints/webhooks.py
Normal 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}
|
Reference in New Issue
Block a user