feat: add DEV_MODE configuration and implement dev-token endpoint for authentication
This commit is contained in:
2
dev.sh
2
dev.sh
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
trap 'kill 0' EXIT
|
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] /') &
|
(cd qwen3-tts-frontend && npm run dev 2>&1 | sed 's/^/[frontend] /') &
|
||||||
|
|
||||||
wait
|
wait
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Annotated
|
from typing import Annotated, Optional
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -20,20 +20,28 @@ from schemas.audiobook import LLMConfigResponse
|
|||||||
|
|
||||||
router = APIRouter(prefix="/auth", tags=["authentication"])
|
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)
|
limiter = Limiter(key_func=get_remote_address)
|
||||||
|
|
||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
token: Annotated[str, Depends(oauth2_scheme)],
|
token: Annotated[Optional[str], Depends(oauth2_scheme)],
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
) -> User:
|
) -> User:
|
||||||
|
if settings.DEV_MODE and not token:
|
||||||
|
user = get_user_by_username(db, username="admin")
|
||||||
|
if user:
|
||||||
|
return user
|
||||||
|
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Could not validate credentials",
|
detail="Could not validate credentials",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if token is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
username = decode_access_token(token)
|
username = decode_access_token(token)
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
@@ -99,6 +107,16 @@ async def login(
|
|||||||
|
|
||||||
return {"access_token": access_token, "token_type": "bearer"}
|
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)
|
@router.get("/me", response_model=User)
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def get_current_user_info(
|
async def get_current_user_info(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Settings(BaseSettings):
|
|||||||
WORKERS: int = Field(default=1)
|
WORKERS: int = Field(default=1)
|
||||||
LOG_LEVEL: str = Field(default="info")
|
LOG_LEVEL: str = Field(default="info")
|
||||||
LOG_FILE: str = Field(default="./app.log")
|
LOG_FILE: str = Field(default="./app.log")
|
||||||
|
DEV_MODE: bool = Field(default=False)
|
||||||
|
|
||||||
RATE_LIMIT_PER_MINUTE: int = Field(default=50)
|
RATE_LIMIT_PER_MINUTE: int = Field(default=50)
|
||||||
RATE_LIMIT_PER_HOUR: int = Field(default=1000)
|
RATE_LIMIT_PER_HOUR: int = Field(default=1000)
|
||||||
@@ -60,7 +61,10 @@ class Settings(BaseSettings):
|
|||||||
return v
|
return v
|
||||||
|
|
||||||
def validate(self):
|
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.")
|
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)
|
Path(self.CACHE_DIR).mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
@@ -22,7 +22,15 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initAuth = async () => {
|
const initAuth = async () => {
|
||||||
try {
|
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) {
|
if (storedToken) {
|
||||||
setToken(storedToken)
|
setToken(storedToken)
|
||||||
const currentUser = await authApi.getCurrentUser()
|
const currentUser = await authApi.getCurrentUser()
|
||||||
|
|||||||
Reference in New Issue
Block a user