Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ async def lifespan(app: FastAPI):
)

# CORS 中間件
# 從環境變量讀取允許的來源,生產環境應設置具體域名
import os
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:8080").split(",")

app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 生產環境應設置具體域名
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,51 @@ def get_weather(location: str, unit: str = "celsius") -> Dict[str, Any]:
return data

def calculate(expression: str) -> float:
"""安全地計算數學表達式"""
"""安全地計算數學表達式

使用 ast.literal_eval 和 operator 模組的安全方法,
避免 eval() 的安全風險。
"""
import ast
import operator

# 支援的安全運算符
operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}

def safe_eval(node):
"""遞迴安全計算 AST 節點"""
if isinstance(node, ast.Constant): # 數字
return node.value
elif isinstance(node, ast.BinOp): # 二元運算
left = safe_eval(node.left)
right = safe_eval(node.right)
op = operators.get(type(node.op))
if op is None:
raise ValueError(f"不支援的運算符: {type(node.op).__name__}")
return op(left, right)
elif isinstance(node, ast.UnaryOp): # 一元運算
operand = safe_eval(node.operand)
op = operators.get(type(node.op))
if op is None:
raise ValueError(f"不支援的運算符: {type(node.op).__name__}")
return op(operand)
else:
raise ValueError(f"不支援的表達式類型: {type(node).__name__}")

try:
# 注意:在生產環境中應該使用更安全的方法
return eval(expression, {"__builtins__": {}})
except:
return "計算錯誤"
# 解析表達式為 AST
tree = ast.parse(expression, mode='eval')
return safe_eval(tree.body)
except Exception as e:
return f"計算錯誤: {e}"

# 可用的函數映射
available_functions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,26 @@ def init_clients():


def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)) -> str:
"""驗證 API Key"""
expected_key = os.getenv("API_KEY", "your-secret-key")
"""驗證 API Key

if credentials.credentials != expected_key:
logger.warning(f"無效的 API Key 嘗試")
使用 secrets.compare_digest 進行常數時間比較,
防止時序攻擊(timing attack)。
"""
import secrets

expected_key = os.getenv("API_KEY")

# 確保 API_KEY 已設置
if not expected_key:
logger.error("API_KEY 環境變量未設置")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Server configuration error"
)

# 使用常數時間比較防止時序攻擊
if not secrets.compare_digest(credentials.credentials, expected_key):
logger.warning("無效的 API Key 嘗試")
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid API Key"
Expand Down
34 changes: 29 additions & 5 deletions 5.AI研究前沿_2024-2025/實戰項目/RAG-ChatBot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,50 @@
from fastapi.responses import HTMLResponse, StreamingResponse
from pydantic import BaseModel
from typing import List, Optional
from contextlib import asynccontextmanager
import uvicorn
import asyncio
from rag_engine import RAGEngine
from middleware.rate_limiter import rate_limiter, rate_limit_middleware


# 生命週期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
"""應用生命週期管理"""
# 啟動時
await rate_limiter.start()
yield
# 關閉時
await rate_limiter.stop()


# 創建 FastAPI 應用
app = FastAPI(
title="RAG ChatBot API",
description="檢索增強生成聊天機器人",
version="1.0.0"
version="1.0.0",
lifespan=lifespan
)

# CORS 配置
# CORS 配置 - 從環境變量讀取允許的來源
import os
ALLOWED_ORIGINS = os.getenv(
"ALLOWED_ORIGINS",
"http://localhost:3000,http://localhost:8080,http://127.0.0.1:3000"
).split(",")

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
)

# 速率限制中間件
app.middleware("http")(rate_limit_middleware)

# 初始化 RAG 引擎
rag_engine = RAGEngine()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
中間件模組

提供 FastAPI 應用的各種中間件功能。
"""

from .rate_limiter import (
RateLimiter,
rate_limiter,
rate_limit,
rate_limit_middleware
)

__all__ = [
'RateLimiter',
'rate_limiter',
'rate_limit',
'rate_limit_middleware'
]
Loading
Loading