import json import random from typing import Tuple, Dict import httpx import redis.asyncio as redis_asyncio from fastapi import APIRouter, Depends, HTTPException from app.models import User from app.schemas.trans_schemas import TransResponse, TransRequest from app.utils.md5 import make_md5 from app.utils.security import is_admin_user, get_current_user from settings import settings translator_router = APIRouter() # For list of language codes, please refer to `https://api.fanyi.baidu.com/doc/21` # endpoint = 'https://api.fanyi.baidu.com' # path = '/api/trans/vip/translate' # url = endpoint + path # # query = 'Hello World! This is 1st paragraph.\nThis is 2nd paragraph.' # # salt = random.randint(32768, 65536) # sign = make_md5(appid + query + str(salt) + appkey) # # headers = {'Content-Type': 'application/x-www-form-urlencoded'} # payload = {'appid': appid, 'q': query, 'from': from_lang, 'to': to_lang, 'salt': salt, 'sign': sign} # # # # Send request # r = requests.post(url, params=payload, headers=headers) # result = r.json() # # # Show response # print(json.dumps(result, indent=4, ensure_ascii=False)) async def baidu_translation(query: str, from_lang: str, to_lang: str): url = "http://api.fanyi.baidu.com/api/trans/vip/translate" appid = settings.BAIDU_APPID appkey = settings.BAIDU_APPKEY salt = str(random.randint(32768, 65536)) sign = make_md5(appid + query + salt + appkey) payload = { "q": query, "from": from_lang, "to": to_lang, "appid": appid, "salt": salt, "sign": sign, } # print(payload) # # request = httpx.Request( # "POST", # url, # data=payload, # headers={"Content-Type": "application/x-www-form-urlencoded"} # ) # print("完整请求内容:") # print("URL:", request.url) # print("Headers:", request.headers) # print("Body:", request.content.decode("utf-8")) async with httpx.AsyncClient(timeout=10) as client: response = await client.post( url, data=payload, headers={'Content-Type': 'application/x-www-form-urlencoded'} ) if response.status_code != 200: raise HTTPException(status_code=500, detail=response.json()) data = response.json() print(json.dumps(data, indent=2, ensure_ascii=False)) if "trans_result" not in data: raise HTTPException(status_code=500, detail={"error_code": data.get("error_code"), "error_msg": data.get("error_msg")}) return "\n".join([item["dst"] for item in data["trans_result"]]) redis = redis_asyncio.from_url("redis://localhost", encoding="utf-8", decode_responses=True) async def rate_limiter( user: Tuple[User, Dict] = Depends(get_current_user), limit: int = 2, window: int = 1 ): """ 限制每个 IP 在 window 秒内最多 limit 次请求 """ client_ip = user[0].id key = f"rate limit {client_ip}" count = await redis.get(key) if count is None: # 第一次请求 → 设置计数和过期时间 await redis.set(key, 1, ex=window) elif int(count) < limit: await redis.incr(key) else: raise HTTPException(status_code=429, detail=f"Too many requests") @translator_router.post('/translate', response_model=TransResponse, dependencies=[Depends(rate_limiter)]) async def translate( translate_request: TransRequest, user: Tuple[User, Dict] = Depends(get_current_user), ): try: text = await baidu_translation( query=translate_request.query, from_lang=translate_request.from_lang, to_lang=translate_request.to_lang, ) except HTTPException as e: print(e.status_code, e.detail) raise HTTPException(status_code=400, detail=e.detail) return TransResponse(translated_text=text) @translator_router.post('/translate/debug', dependencies=[Depends(rate_limiter)]) async def test_translate( query: str, from_lang: str = "auto", to_lang: str = 'zh', admin_user: Tuple[User, dict] = Depends(is_admin_user) ): """ 尝试使用多次翻译请求接口,要求在前端监听输入在约300ms左右内保持不输入再传入, 同时后端检查避免多次恶意访问 :param query: 待翻译文本 :param from_lang: 源语言,默认auto,百度API自动检测 :param to_lang: 目标语言,不允许auto :param admin_user: 测试接口,仅管理员权限可用 :return: """ raw = await baidu_translation(query, from_lang, to_lang) print(raw) return TransResponse(translated_text=raw)