Files
disknext/routers/api/v1/callback/__init__.py
于小丘 7200df6d87
All checks were successful
Test / test (push) Successful in 2m11s
fix: patch storage quota bypass and harden auth security
- Fix WebDAV chunked PUT bypassing storage quota when remaining_quota <= 0
- Add QuotaLimitedWriter to enforce quota during streaming writes
- Clean up residual files on write failure in end_write()
- Add Magic Link replay attack prevention via TokenStore
- Reject startup when JWT SECRET_KEY is not configured
- Sanitize OAuth callback and Magic Link log output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 22:20:43 +08:00

301 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, Query
from fastapi.responses import PlainTextResponse
from loguru import logger as l
from sqlmodels import ResponseBase
import service.oauth
from utils import http_exceptions
callback_router = APIRouter(
prefix='/callback',
tags=["callback"],
)
oauth_router = APIRouter(
prefix='/callback/oauth',
tags=["callback", "oauth"],
)
pay_router = APIRouter(
prefix='/callback/pay',
tags=["callback", "pay"],
)
upload_router = APIRouter(
prefix='/callback/upload',
tags=["callback", "upload"],
)
callback_router.include_router(oauth_router)
callback_router.include_router(pay_router)
callback_router.include_router(upload_router)
@oauth_router.post(
path='/qq',
summary='QQ互联回调',
description='Handle QQ OAuth callback and return user information.',
)
def router_callback_qq() -> ResponseBase:
"""
Handle QQ OAuth callback and return user information.
Returns:
ResponseBase: A model containing the response data for the QQ OAuth callback.
"""
http_exceptions.raise_not_implemented()
@oauth_router.get(
path='/github',
summary='GitHub OAuth 回调',
description='Handle GitHub OAuth callback and return user information.',
)
async def router_callback_github(
code: str = Query(description="The token received from GitHub for authentication.")) -> PlainTextResponse:
"""
GitHub OAuth 回调处理
- 错误响应示例:
- {
'error': 'bad_verification_code',
'error_description': 'The code passed is incorrect or expired.',
'error_uri': 'https://docs.github.com/apps/managing-oauth-apps/troubleshooting-oauth-app-access-token-request-errors/#bad-verification-code'
}
Returns:
PlainTextResponse: A response containing the user information from GitHub.
"""
try:
access_token = await service.oauth.github.get_access_token(code)
if not access_token:
return PlainTextResponse("GitHub 认证失败", status_code=400)
user_data = await service.oauth.github.get_user_info(access_token.access_token)
# [TODO] 把 access_token 和 user_data 写数据库,生成 JWT重定向到前端
l.info(f"GitHub OAuth 回调成功: user={user_data.user_data.login}")
return PlainTextResponse("认证成功,功能开发中", status_code=200)
except Exception as e:
l.error(f"GitHub OAuth 回调异常: {e}")
return PlainTextResponse("认证过程中发生错误,请重试", status_code=500)
@pay_router.post(
path='/alipay',
summary='支付宝支付回调',
description='Handle Alipay payment callback and return payment status.',
)
def router_callback_alipay() -> ResponseBase:
"""
Handle Alipay payment callback and return payment status.
Returns:
ResponseBase: A model containing the response data for the Alipay payment callback.
"""
http_exceptions.raise_not_implemented()
@pay_router.post(
path='/wechat',
summary='微信支付回调',
description='Handle WeChat Pay payment callback and return payment status.',
)
def router_callback_wechat() -> ResponseBase:
"""
Handle WeChat Pay payment callback and return payment status.
Returns:
ResponseBase: A model containing the response data for the WeChat Pay payment callback.
"""
http_exceptions.raise_not_implemented()
@pay_router.post(
path='/stripe',
summary='Stripe支付回调',
description='Handle Stripe payment callback and return payment status.',
)
def router_callback_stripe() -> ResponseBase:
"""
Handle Stripe payment callback and return payment status.
Returns:
ResponseBase: A model containing the response data for the Stripe payment callback.
"""
http_exceptions.raise_not_implemented()
@pay_router.get(
path='/easypay',
summary='易支付回调',
description='Handle EasyPay payment callback and return payment status.',
)
def router_callback_easypay() -> PlainTextResponse:
"""
Handle EasyPay payment callback and return payment status.
Returns:
PlainTextResponse: A response containing the payment status for the EasyPay payment callback.
"""
http_exceptions.raise_not_implemented()
# return PlainTextResponse("success", status_code=200)
@pay_router.get(
path='/custom/{order_no}/{id}',
summary='自定义支付回调',
description='Handle custom payment callback and return payment status.',
)
def router_callback_custom(order_no: str, id: str) -> ResponseBase:
"""
Handle custom payment callback and return payment status.
Args:
order_no (str): The order number for the payment.
id (str): The ID associated with the payment.
Returns:
ResponseBase: A model containing the response data for the custom payment callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/remote/{session_id}/{key}',
summary='远程上传回调',
description='Handle remote upload callback and return upload status.',
)
def router_callback_remote(session_id: str, key: str) -> ResponseBase:
"""
Handle remote upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
key (str): The key for the uploaded file.
Returns:
ResponseBase: A model containing the response data for the remote upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/qiniu/{session_id}',
summary='七牛云上传回调',
description='Handle Qiniu Cloud upload callback and return upload status.',
)
def router_callback_qiniu(session_id: str) -> ResponseBase:
"""
Handle Qiniu Cloud upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the Qiniu Cloud upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/tencent/{session_id}',
summary='腾讯云上传回调',
description='Handle Tencent Cloud upload callback and return upload status.',
)
def router_callback_tencent(session_id: str) -> ResponseBase:
"""
Handle Tencent Cloud upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the Tencent Cloud upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/aliyun/{session_id}',
summary='阿里云上传回调',
description='Handle Aliyun upload callback and return upload status.',
)
def router_callback_aliyun(session_id: str) -> ResponseBase:
"""
Handle Aliyun upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the Aliyun upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/upyun/{session_id}',
summary='又拍云上传回调',
description='Handle Upyun upload callback and return upload status.',
)
def router_callback_upyun(session_id: str) -> ResponseBase:
"""
Handle Upyun upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the Upyun upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/aws/{session_id}',
summary='AWS S3上传回调',
description='Handle AWS S3 upload callback and return upload status.',
)
def router_callback_aws(session_id: str) -> ResponseBase:
"""
Handle AWS S3 upload callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the AWS S3 upload callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.post(
path='/onedrive/finish/{session_id}',
summary='OneDrive上传完成回调',
description='Handle OneDrive upload completion callback and return upload status.',
)
def router_callback_onedrive_finish(session_id: str) -> ResponseBase:
"""
Handle OneDrive upload completion callback and return upload status.
Args:
session_id (str): The session ID for the upload.
Returns:
ResponseBase: A model containing the response data for the OneDrive upload completion callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.get(
path='/ondrive/auth',
summary='OneDrive授权回调',
description='Handle OneDrive authorization callback and return authorization status.',
)
def router_callback_onedrive_auth() -> ResponseBase:
"""
Handle OneDrive authorization callback and return authorization status.
Returns:
ResponseBase: A model containing the response data for the OneDrive authorization callback.
"""
http_exceptions.raise_not_implemented()
@upload_router.get(
path='/google/auth',
summary='Google OAuth 完成',
description='Handle Google OAuth completion callback and return authorization status.',
)
def router_callback_google_auth() -> ResponseBase:
"""
Handle Google OAuth completion callback and return authorization status.
Returns:
ResponseBase: A model containing the response data for the Google OAuth completion callback.
"""
http_exceptions.raise_not_implemented()