From d170ba336277d42758d5c7329b4cc8b883ae4502 Mon Sep 17 00:00:00 2001 From: bdim404 Date: Tue, 7 Apr 2026 10:39:07 +0800 Subject: [PATCH] feat: add DEV_MODE configuration and implement dev-token endpoint for authentication --- dev.sh | 2 +- qwen3-tts-backend/api/auth.py | 24 ++++++++++++++++--- qwen3-tts-backend/config.py | 6 ++++- .../src/contexts/AuthContext.tsx | 10 +++++++- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/dev.sh b/dev.sh index 3096be3..8b8991f 100755 --- a/dev.sh +++ b/dev.sh @@ -1,7 +1,7 @@ #!/bin/bash trap 'kill 0' EXIT -(cd qwen3-tts-backend && /home/bdim/miniconda3/envs/qwen3-tts/bin/uvicorn main:app --host 0.0.0.0 --port 8000 --reload 2>&1 | sed 's/^/[backend] /') & +(cd qwen3-tts-backend && DEV_MODE=true LOG_LEVEL=debug /home/bdim/miniconda3/envs/qwen3-tts/bin/uvicorn main:app --host 0.0.0.0 --port 8000 --reload --log-level debug 2>&1 | sed 's/^/[backend] /') & (cd qwen3-tts-frontend && npm run dev 2>&1 | sed 's/^/[frontend] /') & wait diff --git a/qwen3-tts-backend/api/auth.py b/qwen3-tts-backend/api/auth.py index 2b0f3d5..063f8e8 100644 --- a/qwen3-tts-backend/api/auth.py +++ b/qwen3-tts-backend/api/auth.py @@ -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( diff --git a/qwen3-tts-backend/config.py b/qwen3-tts-backend/config.py index 14582b3..b71d05a 100644 --- a/qwen3-tts-backend/config.py +++ b/qwen3-tts-backend/config.py @@ -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) diff --git a/qwen3-tts-frontend/src/contexts/AuthContext.tsx b/qwen3-tts-frontend/src/contexts/AuthContext.tsx index 256d863..93d432c 100644 --- a/qwen3-tts-frontend/src/contexts/AuthContext.tsx +++ b/qwen3-tts-frontend/src/contexts/AuthContext.tsx @@ -22,7 +22,15 @@ export function AuthProvider({ children }: { children: ReactNode }) { useEffect(() => { const initAuth = async () => { try { - const storedToken = localStorage.getItem('token') + let storedToken = localStorage.getItem('token') + if (!storedToken && import.meta.env.DEV) { + const res = await fetch('/api/auth/dev-token') + if (res.ok) { + const data = await res.json() + storedToken = data.access_token + localStorage.setItem('token', storedToken!) + } + } if (storedToken) { setToken(storedToken) const currentUser = await authApi.getCurrentUser()