feat: add DEV_MODE configuration and implement dev-token endpoint for authentication

This commit is contained in:
2026-04-07 10:39:07 +08:00
parent d12c1223f9
commit d170ba3362
4 changed files with 36 additions and 6 deletions

View File

@@ -1,5 +1,5 @@
from datetime import timedelta
from typing import Annotated
from typing import Annotated, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Request
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
@@ -20,20 +20,28 @@ from schemas.audiobook import LLMConfigResponse
router = APIRouter(prefix="/auth", tags=["authentication"])
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token", auto_error=not settings.DEV_MODE)
limiter = Limiter(key_func=get_remote_address)
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
token: Annotated[Optional[str], Depends(oauth2_scheme)],
db: Session = Depends(get_db)
) -> User:
if settings.DEV_MODE and not token:
user = get_user_by_username(db, username="admin")
if user:
return user
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
if token is None:
raise credentials_exception
username = decode_access_token(token)
if username is None:
raise credentials_exception
@@ -99,6 +107,16 @@ async def login(
return {"access_token": access_token, "token_type": "bearer"}
@router.get("/dev-token", response_model=Token)
async def dev_token(db: Session = Depends(get_db)):
if not settings.DEV_MODE:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not available outside DEV_MODE")
user = get_user_by_username(db, username="admin")
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Admin user not found")
access_token = create_access_token(data={"sub": user.username})
return {"access_token": access_token, "token_type": "bearer"}
@router.get("/me", response_model=User)
@limiter.limit("30/minute")
async def get_current_user_info(

View File

@@ -25,6 +25,7 @@ class Settings(BaseSettings):
WORKERS: int = Field(default=1)
LOG_LEVEL: str = Field(default="info")
LOG_FILE: str = Field(default="./app.log")
DEV_MODE: bool = Field(default=False)
RATE_LIMIT_PER_MINUTE: int = Field(default=50)
RATE_LIMIT_PER_HOUR: int = Field(default=1000)
@@ -60,7 +61,10 @@ class Settings(BaseSettings):
return v
def validate(self):
if self.SECRET_KEY == "your-secret-key-change-this-in-production":
if self.DEV_MODE:
import warnings
warnings.warn("DEV_MODE is enabled — authentication is bypassed. Do NOT use in production.")
elif self.SECRET_KEY == "your-secret-key-change-this-in-production":
raise ValueError("Insecure default SECRET_KEY is not allowed. Please set a strong SECRET_KEY in environment.")
Path(self.CACHE_DIR).mkdir(parents=True, exist_ok=True)