feat: add user preferences migration and context

This commit is contained in:
2026-02-03 16:09:50 +08:00
parent abe0dc131b
commit 555bf38b71
21 changed files with 931 additions and 79 deletions

View File

@@ -14,8 +14,8 @@ from core.security import (
decode_access_token
)
from db.database import get_db
from db.crud import get_user_by_username, get_user_by_email, create_user, change_user_password, update_user_aliyun_key
from schemas.user import User, UserCreate, Token, PasswordChange, AliyunKeyUpdate, AliyunKeyVerifyResponse
from db.crud import get_user_by_username, get_user_by_email, create_user, change_user_password, update_user_aliyun_key, get_user_preferences, update_user_preferences
from schemas.user import User, UserCreate, Token, PasswordChange, AliyunKeyUpdate, AliyunKeyVerifyResponse, UserPreferences, UserPreferencesResponse
router = APIRouter(prefix="/auth", tags=["authentication"])
@@ -174,6 +174,32 @@ async def set_aliyun_key(
return user
@router.delete("/aliyun-key")
@limiter.limit("5/minute")
async def delete_aliyun_key(
request: Request,
current_user: Annotated[User, Depends(get_current_user)],
db: Session = Depends(get_db)
):
user = update_user_aliyun_key(
db,
user_id=current_user.id,
encrypted_api_key=None
)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
prefs = get_user_preferences(db, current_user.id)
if prefs.get("default_backend") == "aliyun":
prefs["default_backend"] = "local"
update_user_preferences(db, current_user.id, prefs)
return {"message": "Aliyun API key deleted", "preferences_updated": True}
@router.get("/aliyun-key/verify", response_model=AliyunKeyVerifyResponse)
@limiter.limit("10/minute")
async def verify_aliyun_key(
@@ -211,3 +237,29 @@ async def verify_aliyun_key(
valid=False,
message="Aliyun API key is not working. Please check your API key."
)
@router.get("/preferences", response_model=UserPreferencesResponse)
@limiter.limit("30/minute")
async def get_preferences(
request: Request,
current_user: Annotated[User, Depends(get_current_user)],
db: Session = Depends(get_db)
):
prefs = get_user_preferences(db, current_user.id)
return UserPreferencesResponse(**prefs)
@router.put("/preferences")
@limiter.limit("10/minute")
async def update_preferences(
request: Request,
preferences: UserPreferences,
current_user: Annotated[User, Depends(get_current_user)],
db: Session = Depends(get_db)
):
user = update_user_preferences(db, current_user.id, preferences.dict())
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return {"message": "Preferences updated"}

View File

@@ -106,7 +106,7 @@ def change_user_password(
def update_user_aliyun_key(
db: Session,
user_id: int,
encrypted_api_key: str
encrypted_api_key: Optional[str]
) -> Optional[User]:
user = get_user_by_id(db, user_id)
if not user:
@@ -229,3 +229,19 @@ def delete_cache_entry(db: Session, cache_id: int, user_id: int) -> bool:
db.delete(cache)
db.commit()
return True
def get_user_preferences(db: Session, user_id: int) -> dict:
user = get_user_by_id(db, user_id)
if not user or not user.user_preferences:
return {"default_backend": "local", "onboarding_completed": False}
return user.user_preferences
def update_user_preferences(db: Session, user_id: int, preferences: dict) -> Optional[User]:
user = get_user_by_id(db, user_id)
if not user:
return None
user.user_preferences = preferences
user.updated_at = datetime.utcnow()
db.commit()
db.refresh(user)
return user

View File

@@ -21,6 +21,7 @@ class User(Base):
is_active = Column(Boolean, default=True, nullable=False)
is_superuser = Column(Boolean, default=False, nullable=False)
aliyun_api_key = Column(Text, nullable=True)
user_preferences = Column(JSON, nullable=True, default=lambda: {"default_backend": "local", "onboarding_completed": False})
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

View File

@@ -0,0 +1,37 @@
import sqlite3
import json
import os
DB_PATH = os.path.join(os.path.dirname(__file__), "qwen_tts.db")
def migrate():
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
try:
cursor.execute("SELECT user_preferences FROM users LIMIT 1")
print("Column user_preferences already exists, skipping migration")
except sqlite3.OperationalError:
print("Adding user_preferences column to users table...")
cursor.execute("""
ALTER TABLE users
ADD COLUMN user_preferences TEXT DEFAULT NULL
""")
cursor.execute("SELECT id FROM users")
user_ids = cursor.fetchall()
default_prefs = json.dumps({"default_backend": "local", "onboarding_completed": False})
for (user_id,) in user_ids:
cursor.execute(
"UPDATE users SET user_preferences = ? WHERE id = ?",
(default_prefs, user_id)
)
conn.commit()
print(f"Migration completed: Added user_preferences column and initialized {len(user_ids)} users")
conn.close()
if __name__ == "__main__":
migrate()

View File

@@ -118,3 +118,11 @@ class AliyunKeyUpdate(BaseModel):
class AliyunKeyVerifyResponse(BaseModel):
valid: bool
message: str
class UserPreferences(BaseModel):
default_backend: str = Field(default="local", pattern="^(local|aliyun)$")
onboarding_completed: bool = Field(default=False)
class UserPreferencesResponse(BaseModel):
default_backend: str
onboarding_completed: bool