From 5a22351a66aaa87fd4c78b371053c176ca9d3c34 Mon Sep 17 00:00:00 2001 From: bdim404 Date: Tue, 3 Feb 2026 16:37:05 +0800 Subject: [PATCH] feat: update user preferences and system settings management --- qwen3-tts-backend/api/auth.py | 34 ++++++-- qwen3-tts-backend/api/tts.py | 81 +++++++++++++----- qwen3-tts-backend/api/users.py | 46 +++++++++- qwen3-tts-backend/db/crud.py | 28 +++++- qwen3-tts-backend/db/models.py | 10 ++- qwen3-tts-backend/migrate_user_preferences.py | 37 -------- qwen3-tts-backend/schemas/user.py | 13 ++- .../scripts/migrate_add_system_settings.py | 70 +++++++++++++++ qwen3-tts-frontend/index.html | 2 +- qwen3-tts-frontend/package-lock.json | 30 +++++++ qwen3-tts-frontend/package.json | 1 + qwen3-tts-frontend/public/favicon.ico | Bin 0 -> 4861 bytes qwen3-tts-frontend/public/qwen.svg | 16 +++- .../src/components/tts/CustomVoiceForm.tsx | 24 ------ .../src/components/tts/VoiceCloneForm.tsx | 24 ------ .../src/components/tts/VoiceDesignForm.tsx | 25 ------ .../src/components/ui/switch.tsx | 27 ++++++ .../src/contexts/UserPreferencesContext.tsx | 11 ++- qwen3-tts-frontend/src/lib/api.ts | 11 ++- qwen3-tts-frontend/src/pages/Settings.tsx | 75 ++++++++++++++-- qwen3-tts-frontend/src/types/auth.ts | 5 ++ 21 files changed, 417 insertions(+), 153 deletions(-) delete mode 100644 qwen3-tts-backend/migrate_user_preferences.py create mode 100644 qwen3-tts-backend/scripts/migrate_add_system_settings.py create mode 100644 qwen3-tts-frontend/public/favicon.ico create mode 100644 qwen3-tts-frontend/src/components/ui/switch.tsx diff --git a/qwen3-tts-backend/api/auth.py b/qwen3-tts-backend/api/auth.py index 1d4cdde..6c0b170 100644 --- a/qwen3-tts-backend/api/auth.py +++ b/qwen3-tts-backend/api/auth.py @@ -14,7 +14,7 @@ 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, get_user_preferences, update_user_preferences +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, is_local_model_enabled from schemas.user import User, UserCreate, Token, PasswordChange, AliyunKeyUpdate, AliyunKeyVerifyResponse, UserPreferences, UserPreferencesResponse router = APIRouter(prefix="/auth", tags=["authentication"]) @@ -246,7 +246,17 @@ async def get_preferences( db: Session = Depends(get_db) ): prefs = get_user_preferences(db, current_user.id) - return UserPreferencesResponse(**prefs) + + local_enabled = is_local_model_enabled(db) + available_backends = ["aliyun"] + if local_enabled or current_user.is_superuser: + available_backends.append("local") + + return { + "default_backend": prefs.get("default_backend", "aliyun"), + "onboarding_completed": prefs.get("onboarding_completed", False), + "available_backends": available_backends + } @router.put("/preferences") @limiter.limit("10/minute") @@ -256,10 +266,24 @@ async def update_preferences( 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: + if preferences.default_backend == "local": + local_enabled = is_local_model_enabled(db) + if not local_enabled and not current_user.is_superuser: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Local model is not available. Please contact administrator." + ) + + updated_user = update_user_preferences( + db, + current_user.id, + preferences.model_dump() + ) + + if not updated_user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) - return {"message": "Preferences updated"} + + return {"message": "Preferences updated successfully"} diff --git a/qwen3-tts-backend/api/tts.py b/qwen3-tts-backend/api/tts.py index a584c9c..b224ad5 100644 --- a/qwen3-tts-backend/api/tts.py +++ b/qwen3-tts-backend/api/tts.py @@ -285,14 +285,27 @@ async def create_custom_voice_job( db: Session = Depends(get_db) ): from core.security import decrypt_api_key + from db.crud import get_user_preferences, is_local_model_enabled - backend_type = req_data.backend or settings.DEFAULT_BACKEND - if backend_type == "aliyun": - if not current_user.aliyun_api_key: - raise HTTPException(status_code=400, detail="Aliyun API key not configured. Please set your API key first.") - user_api_key = decrypt_api_key(current_user.aliyun_api_key) - if not user_api_key: - raise HTTPException(status_code=400, detail="Invalid Aliyun API key. Please update your API key.") + user_prefs = get_user_preferences(db, current_user.id) + preferred_backend = user_prefs.get("default_backend", "aliyun") + + local_enabled = is_local_model_enabled(db) + can_use_local = local_enabled or current_user.is_superuser + + backend_type = req_data.backend if hasattr(req_data, 'backend') and req_data.backend else preferred_backend + + if backend_type == "local" and not can_use_local: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Local model is not available. Please contact administrator." + ) + + if backend_type == "aliyun" and not current_user.aliyun_api_key: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Aliyun API key not configured. Please set your API key in Settings." + ) try: validate_text_length(req_data.text) @@ -362,14 +375,27 @@ async def create_voice_design_job( db: Session = Depends(get_db) ): from core.security import decrypt_api_key + from db.crud import get_user_preferences, is_local_model_enabled - backend_type = req_data.backend or settings.DEFAULT_BACKEND - if backend_type == "aliyun": - if not current_user.aliyun_api_key: - raise HTTPException(status_code=400, detail="Aliyun API key not configured. Please set your API key first.") - user_api_key = decrypt_api_key(current_user.aliyun_api_key) - if not user_api_key: - raise HTTPException(status_code=400, detail="Invalid Aliyun API key. Please update your API key.") + user_prefs = get_user_preferences(db, current_user.id) + preferred_backend = user_prefs.get("default_backend", "aliyun") + + local_enabled = is_local_model_enabled(db) + can_use_local = local_enabled or current_user.is_superuser + + backend_type = req_data.backend if hasattr(req_data, 'backend') and req_data.backend else preferred_backend + + if backend_type == "local" and not can_use_local: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Local model is not available. Please contact administrator." + ) + + if backend_type == "aliyun" and not current_user.aliyun_api_key: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Aliyun API key not configured. Please set your API key in Settings." + ) try: validate_text_length(req_data.text) @@ -450,14 +476,27 @@ async def create_voice_clone_job( db: Session = Depends(get_db) ): from core.security import decrypt_api_key + from db.crud import get_user_preferences, is_local_model_enabled - backend_type = backend or settings.DEFAULT_BACKEND - if backend_type == "aliyun": - if not current_user.aliyun_api_key: - raise HTTPException(status_code=400, detail="Aliyun API key not configured. Please set your API key first.") - user_api_key = decrypt_api_key(current_user.aliyun_api_key) - if not user_api_key: - raise HTTPException(status_code=400, detail="Invalid Aliyun API key. Please update your API key.") + user_prefs = get_user_preferences(db, current_user.id) + preferred_backend = user_prefs.get("default_backend", "aliyun") + + local_enabled = is_local_model_enabled(db) + can_use_local = local_enabled or current_user.is_superuser + + backend_type = backend if backend else preferred_backend + + if backend_type == "local" and not can_use_local: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Local model is not available. Please contact administrator." + ) + + if backend_type == "aliyun" and not current_user.aliyun_api_key: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Aliyun API key not configured. Please set your API key in Settings." + ) try: validate_text_length(text) diff --git a/qwen3-tts-backend/api/users.py b/qwen3-tts-backend/api/users.py index 2a5e074..e2030a5 100644 --- a/qwen3-tts-backend/api/users.py +++ b/qwen3-tts-backend/api/users.py @@ -15,9 +15,12 @@ from db.crud import ( list_users, create_user_by_admin, update_user, - delete_user + delete_user, + get_system_setting, + update_system_setting, + is_local_model_enabled ) -from schemas.user import User, UserCreateByAdmin, UserUpdate, UserListResponse +from schemas.user import User, UserCreateByAdmin, UserUpdate, UserListResponse, SystemSettingsUpdate, SystemSettingsResponse router = APIRouter(prefix="/users", tags=["users"]) limiter = Limiter(key_func=get_remote_address) @@ -167,3 +170,42 @@ async def delete_user_by_id( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) + +@router.get("/system/settings", response_model=SystemSettingsResponse) +async def get_system_settings( + current_user: Annotated[User, Depends(require_superuser)], + db: Session = Depends(get_db) +): + local_enabled = is_local_model_enabled(db) + return {"local_model_enabled": local_enabled} + +@router.put("/system/settings") +async def update_system_settings( + settings: SystemSettingsUpdate, + current_user: Annotated[User, Depends(require_superuser)], + db: Session = Depends(get_db) +): + from db.models import User + from datetime import datetime + + update_system_setting(db, "local_model_enabled", {"enabled": settings.local_model_enabled}) + + if not settings.local_model_enabled: + users = db.query(User).filter(User.is_superuser == False).all() + migrated_count = 0 + + for user in users: + prefs = user.user_preferences or {} + if prefs.get("default_backend") == "local": + prefs["default_backend"] = "aliyun" + user.user_preferences = prefs + user.updated_at = datetime.utcnow() + migrated_count += 1 + + db.commit() + return { + "message": "System settings updated", + "users_migrated": migrated_count + } + + return {"message": "System settings updated", "users_migrated": 0} diff --git a/qwen3-tts-backend/db/crud.py b/qwen3-tts-backend/db/crud.py index d76f8e1..6e67b91 100644 --- a/qwen3-tts-backend/db/crud.py +++ b/qwen3-tts-backend/db/crud.py @@ -3,7 +3,7 @@ from typing import Optional, List, Dict, Any from datetime import datetime from sqlalchemy.orm import Session -from db.models import User, Job, VoiceCache +from db.models import User, Job, VoiceCache, SystemSettings def get_user_by_username(db: Session, username: str) -> Optional[User]: return db.query(User).filter(User.username == username).first() @@ -233,7 +233,7 @@ def delete_cache_entry(db: Session, cache_id: int, user_id: int) -> bool: 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 {"default_backend": "aliyun", "onboarding_completed": False} return user.user_preferences def update_user_preferences(db: Session, user_id: int, preferences: dict) -> Optional[User]: @@ -245,3 +245,27 @@ def update_user_preferences(db: Session, user_id: int, preferences: dict) -> Opt db.commit() db.refresh(user) return user + +def get_system_setting(db: Session, key: str) -> Optional[dict]: + setting = db.query(SystemSettings).filter(SystemSettings.key == key).first() + if not setting: + return None + return setting.value + +def update_system_setting(db: Session, key: str, value: dict) -> SystemSettings: + setting = db.query(SystemSettings).filter(SystemSettings.key == key).first() + if setting: + setting.value = value + setting.updated_at = datetime.utcnow() + else: + setting = SystemSettings(key=key, value=value, updated_at=datetime.utcnow()) + db.add(setting) + db.commit() + db.refresh(setting) + return setting + +def is_local_model_enabled(db: Session) -> bool: + setting = get_system_setting(db, "local_model_enabled") + if not setting: + return False + return setting.get("enabled", False) diff --git a/qwen3-tts-backend/db/models.py b/qwen3-tts-backend/db/models.py index 96a91fe..60c883c 100644 --- a/qwen3-tts-backend/db/models.py +++ b/qwen3-tts-backend/db/models.py @@ -21,7 +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}) + user_preferences = Column(JSON, nullable=True, default=lambda: {"default_backend": "aliyun", "onboarding_completed": False}) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) @@ -68,3 +68,11 @@ class VoiceCache(Base): __table_args__ = ( Index('idx_user_hash', 'user_id', 'ref_audio_hash'), ) + +class SystemSettings(Base): + __tablename__ = "system_settings" + + id = Column(Integer, primary_key=True, index=True) + key = Column(String(100), unique=True, nullable=False, index=True) + value = Column(JSON, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) diff --git a/qwen3-tts-backend/migrate_user_preferences.py b/qwen3-tts-backend/migrate_user_preferences.py deleted file mode 100644 index 9704168..0000000 --- a/qwen3-tts-backend/migrate_user_preferences.py +++ /dev/null @@ -1,37 +0,0 @@ -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() diff --git a/qwen3-tts-backend/schemas/user.py b/qwen3-tts-backend/schemas/user.py index ad71f0e..1a6f666 100644 --- a/qwen3-tts-backend/schemas/user.py +++ b/qwen3-tts-backend/schemas/user.py @@ -120,9 +120,16 @@ class AliyunKeyVerifyResponse(BaseModel): message: str class UserPreferences(BaseModel): - default_backend: str = Field(default="local", pattern="^(local|aliyun)$") + default_backend: str = Field(default="aliyun", pattern="^(local|aliyun)$") onboarding_completed: bool = Field(default=False) class UserPreferencesResponse(BaseModel): - default_backend: str - onboarding_completed: bool + default_backend: str = Field(default="aliyun", pattern="^(local|aliyun)$") + onboarding_completed: bool = Field(default=False) + available_backends: list[str] = Field(default=["aliyun"]) + +class SystemSettingsUpdate(BaseModel): + local_model_enabled: bool + +class SystemSettingsResponse(BaseModel): + local_model_enabled: bool diff --git a/qwen3-tts-backend/scripts/migrate_add_system_settings.py b/qwen3-tts-backend/scripts/migrate_add_system_settings.py new file mode 100644 index 0000000..11a5569 --- /dev/null +++ b/qwen3-tts-backend/scripts/migrate_add_system_settings.py @@ -0,0 +1,70 @@ +from sqlalchemy import create_engine, text +from sqlalchemy.orm import sessionmaker +from datetime import datetime +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + +from config import settings + +engine = create_engine( + settings.DATABASE_URL, + connect_args={"check_same_thread": False} if "sqlite" in settings.DATABASE_URL else {} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +def upgrade(): + db = SessionLocal() + try: + db.execute(text(""" + CREATE TABLE IF NOT EXISTS system_settings ( + id INTEGER PRIMARY KEY, + key VARCHAR(100) UNIQUE NOT NULL, + value JSON NOT NULL, + updated_at DATETIME NOT NULL + ) + """)) + + db.execute(text(""" + CREATE INDEX IF NOT EXISTS ix_system_settings_key ON system_settings (key) + """)) + + result = db.execute(text( + "SELECT COUNT(*) as count FROM system_settings WHERE key = 'local_model_enabled'" + )) + count = result.fetchone()[0] + + if count == 0: + db.execute(text( + "INSERT INTO system_settings (key, value, updated_at) VALUES " + "('local_model_enabled', '{\"enabled\": false}', :now)" + ), {"now": datetime.utcnow()}) + + db.execute(text(""" + UPDATE users + SET user_preferences = json_set( + COALESCE(user_preferences, '{}'), + '$.default_backend', + 'aliyun' + ) + WHERE json_extract(user_preferences, '$.default_backend') IS NULL + OR json_extract(user_preferences, '$.default_backend') = 'local' + """)) + + db.commit() + print("Migration completed successfully!") + print("- Created system_settings table") + print("- Added local_model_enabled setting (default: false)") + print("- Updated user preferences to use aliyun backend by default") + + except Exception as e: + db.rollback() + print(f"Migration failed: {e}") + raise + finally: + db.close() + +if __name__ == "__main__": + upgrade() diff --git a/qwen3-tts-frontend/index.html b/qwen3-tts-frontend/index.html index 1d40e2e..fc2fcfa 100644 --- a/qwen3-tts-frontend/index.html +++ b/qwen3-tts-frontend/index.html @@ -2,7 +2,7 @@ - + Qwen3-TTS-WebUI diff --git a/qwen3-tts-frontend/package-lock.json b/qwen3-tts-frontend/package-lock.json index 618f181..d77f435 100644 --- a/qwen3-tts-frontend/package-lock.json +++ b/qwen3-tts-frontend/package-lock.json @@ -21,6 +21,7 @@ "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "axios": "^1.13.3", @@ -2019,6 +2020,35 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", diff --git a/qwen3-tts-frontend/package.json b/qwen3-tts-frontend/package.json index 656b8d5..efb79e0 100644 --- a/qwen3-tts-frontend/package.json +++ b/qwen3-tts-frontend/package.json @@ -23,6 +23,7 @@ "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "axios": "^1.13.3", diff --git a/qwen3-tts-frontend/public/favicon.ico b/qwen3-tts-frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b52767d5913893b13734c2ad6592baebf591529 GIT binary patch literal 4861 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 + + + + + + + + + + + + + + diff --git a/qwen3-tts-frontend/src/components/tts/CustomVoiceForm.tsx b/qwen3-tts-frontend/src/components/tts/CustomVoiceForm.tsx index f0cf737..7ab01da 100644 --- a/qwen3-tts-frontend/src/components/tts/CustomVoiceForm.tsx +++ b/qwen3-tts-frontend/src/components/tts/CustomVoiceForm.tsx @@ -33,7 +33,6 @@ const formSchema = z.object({ top_k: z.number().min(1).max(100).optional(), top_p: z.number().min(0).max(1).optional(), repetition_penalty: z.number().min(0).max(2).optional(), - backend: z.string().optional(), }) type FormData = z.infer @@ -77,16 +76,9 @@ const CustomVoiceForm = forwardRef((_props, ref) => { top_k: 20, top_p: 0.7, repetition_penalty: 1.05, - backend: preferences?.default_backend || 'local', }, }) - useEffect(() => { - if (preferences?.default_backend) { - setValue('backend', preferences.default_backend) - } - }, [preferences?.default_backend, setValue]) - useImperativeHandle(ref, () => ({ loadParams: (params: any) => { setValue('text', params.text || '') @@ -142,22 +134,6 @@ const CustomVoiceForm = forwardRef((_props, ref) => { return (
-
- - -
-
setValue('backend', value)} - > - - - - - 本地模型 - 阿里云 API - - -
-
setValue('backend', value)} - > - - - - - 本地模型 - 阿里云 API - - -
-