0
qwen3-tts-backend/db/__init__.py
Normal file
0
qwen3-tts-backend/db/__init__.py
Normal file
198
qwen3-tts-backend/db/crud.py
Normal file
198
qwen3-tts-backend/db/crud.py
Normal file
@@ -0,0 +1,198 @@
|
||||
import json
|
||||
from typing import Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from db.models import User, Job, VoiceCache
|
||||
|
||||
def get_user_by_username(db: Session, username: str) -> Optional[User]:
|
||||
return db.query(User).filter(User.username == username).first()
|
||||
|
||||
def get_user_by_email(db: Session, email: str) -> Optional[User]:
|
||||
return db.query(User).filter(User.email == email).first()
|
||||
|
||||
def create_user(db: Session, username: str, email: str, hashed_password: str) -> User:
|
||||
user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
hashed_password=hashed_password
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
def create_user_by_admin(
|
||||
db: Session,
|
||||
username: str,
|
||||
email: str,
|
||||
hashed_password: str,
|
||||
is_superuser: bool = False
|
||||
) -> User:
|
||||
user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
hashed_password=hashed_password,
|
||||
is_superuser=is_superuser
|
||||
)
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
def get_user_by_id(db: Session, user_id: int) -> Optional[User]:
|
||||
return db.query(User).filter(User.id == user_id).first()
|
||||
|
||||
def list_users(db: Session, skip: int = 0, limit: int = 100) -> tuple[List[User], int]:
|
||||
total = db.query(User).count()
|
||||
users = db.query(User).order_by(User.created_at.desc()).offset(skip).limit(limit).all()
|
||||
return users, total
|
||||
|
||||
def update_user(
|
||||
db: Session,
|
||||
user_id: int,
|
||||
username: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
hashed_password: Optional[str] = None,
|
||||
is_active: Optional[bool] = None,
|
||||
is_superuser: Optional[bool] = None
|
||||
) -> Optional[User]:
|
||||
user = get_user_by_id(db, user_id)
|
||||
if not user:
|
||||
return None
|
||||
|
||||
if username is not None:
|
||||
user.username = username
|
||||
if email is not None:
|
||||
user.email = email
|
||||
if hashed_password is not None:
|
||||
user.hashed_password = hashed_password
|
||||
if is_active is not None:
|
||||
user.is_active = is_active
|
||||
if is_superuser is not None:
|
||||
user.is_superuser = is_superuser
|
||||
|
||||
user.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
def delete_user(db: Session, user_id: int) -> bool:
|
||||
user = get_user_by_id(db, user_id)
|
||||
if not user:
|
||||
return False
|
||||
db.delete(user)
|
||||
db.commit()
|
||||
return True
|
||||
|
||||
def create_job(db: Session, user_id: int, job_type: str, input_data: Dict[str, Any]) -> Job:
|
||||
job = Job(
|
||||
user_id=user_id,
|
||||
job_type=job_type,
|
||||
input_data=json.dumps(input_data),
|
||||
status="pending"
|
||||
)
|
||||
db.add(job)
|
||||
db.commit()
|
||||
db.refresh(job)
|
||||
return job
|
||||
|
||||
def get_job(db: Session, job_id: int, user_id: int) -> Optional[Job]:
|
||||
return db.query(Job).filter(Job.id == job_id, Job.user_id == user_id).first()
|
||||
|
||||
def list_jobs(
|
||||
db: Session,
|
||||
user_id: int,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
status: Optional[str] = None
|
||||
) -> List[Job]:
|
||||
query = db.query(Job).filter(Job.user_id == user_id)
|
||||
if status:
|
||||
query = query.filter(Job.status == status)
|
||||
return query.order_by(Job.created_at.desc()).offset(skip).limit(limit).all()
|
||||
|
||||
def update_job_status(
|
||||
db: Session,
|
||||
job_id: int,
|
||||
user_id: int,
|
||||
status: str,
|
||||
output_path: Optional[str] = None,
|
||||
error_message: Optional[str] = None
|
||||
) -> Optional[Job]:
|
||||
job = get_job(db, job_id, user_id)
|
||||
if not job:
|
||||
return None
|
||||
|
||||
job.status = status
|
||||
if output_path:
|
||||
job.output_path = output_path
|
||||
if error_message:
|
||||
job.error_message = error_message
|
||||
if status in ["completed", "failed"]:
|
||||
job.completed_at = datetime.utcnow()
|
||||
|
||||
db.commit()
|
||||
db.refresh(job)
|
||||
return job
|
||||
|
||||
def delete_job(db: Session, job_id: int, user_id: int) -> bool:
|
||||
job = get_job(db, job_id, user_id)
|
||||
if not job:
|
||||
return False
|
||||
db.delete(job)
|
||||
db.commit()
|
||||
return True
|
||||
|
||||
def create_cache_entry(
|
||||
db: Session,
|
||||
user_id: int,
|
||||
ref_audio_hash: str,
|
||||
cache_path: str,
|
||||
meta_data: Optional[Dict[str, Any]] = None
|
||||
) -> VoiceCache:
|
||||
cache = VoiceCache(
|
||||
user_id=user_id,
|
||||
ref_audio_hash=ref_audio_hash,
|
||||
cache_path=cache_path,
|
||||
meta_data=json.dumps(meta_data) if meta_data else None
|
||||
)
|
||||
db.add(cache)
|
||||
db.commit()
|
||||
db.refresh(cache)
|
||||
return cache
|
||||
|
||||
def get_cache_entry(db: Session, user_id: int, ref_audio_hash: str) -> Optional[VoiceCache]:
|
||||
cache = db.query(VoiceCache).filter(
|
||||
VoiceCache.user_id == user_id,
|
||||
VoiceCache.ref_audio_hash == ref_audio_hash
|
||||
).first()
|
||||
|
||||
if cache:
|
||||
cache.last_accessed = datetime.utcnow()
|
||||
cache.access_count += 1
|
||||
db.commit()
|
||||
db.refresh(cache)
|
||||
|
||||
return cache
|
||||
|
||||
def list_cache_entries(
|
||||
db: Session,
|
||||
user_id: int,
|
||||
skip: int = 0,
|
||||
limit: int = 100
|
||||
) -> List[VoiceCache]:
|
||||
return db.query(VoiceCache).filter(
|
||||
VoiceCache.user_id == user_id
|
||||
).order_by(VoiceCache.last_accessed.desc()).offset(skip).limit(limit).all()
|
||||
|
||||
def delete_cache_entry(db: Session, cache_id: int, user_id: int) -> bool:
|
||||
cache = db.query(VoiceCache).filter(
|
||||
VoiceCache.id == cache_id,
|
||||
VoiceCache.user_id == user_id
|
||||
).first()
|
||||
if not cache:
|
||||
return False
|
||||
db.delete(cache)
|
||||
db.commit()
|
||||
return True
|
||||
23
qwen3-tts-backend/db/database.py
Normal file
23
qwen3-tts-backend/db/database.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||
|
||||
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)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def init_db():
|
||||
Base.metadata.create_all(bind=engine)
|
||||
67
qwen3-tts-backend/db/models.py
Normal file
67
qwen3-tts-backend/db/models.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, Index, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from db.database import Base
|
||||
|
||||
class JobStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
username = Column(String(50), unique=True, nullable=False, index=True)
|
||||
email = Column(String(255), unique=True, nullable=False, index=True)
|
||||
hashed_password = Column(String(255), nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
is_superuser = Column(Boolean, default=False, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
jobs = relationship("Job", back_populates="user", cascade="all, delete-orphan")
|
||||
voice_caches = relationship("VoiceCache", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
class Job(Base):
|
||||
__tablename__ = "jobs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
job_type = Column(String(50), nullable=False)
|
||||
status = Column(String(50), default="pending", nullable=False, index=True)
|
||||
input_data = Column(Text, nullable=True)
|
||||
input_params = Column(JSON, nullable=True)
|
||||
output_path = Column(String(500), nullable=True)
|
||||
error_message = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
started_at = Column(DateTime, nullable=True)
|
||||
completed_at = Column(DateTime, nullable=True)
|
||||
|
||||
user = relationship("User", back_populates="jobs")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_user_status', 'user_id', 'status'),
|
||||
Index('idx_user_created', 'user_id', 'created_at'),
|
||||
)
|
||||
|
||||
class VoiceCache(Base):
|
||||
__tablename__ = "voice_caches"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
ref_audio_hash = Column(String(64), nullable=False, index=True)
|
||||
cache_path = Column(String(500), nullable=False)
|
||||
meta_data = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
last_accessed = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
access_count = Column(Integer, default=0, nullable=False)
|
||||
|
||||
user = relationship("User", back_populates="voice_caches")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_user_hash', 'user_id', 'ref_audio_hash'),
|
||||
)
|
||||
Reference in New Issue
Block a user