Features: - Image generation (OpenAI, Gemini, Leonardo, Bria, Stability, Flux) - Nano Banana iterative editing - Video generation and upscaling - Audio TTS, STT, sound effects (ElevenLabs) - Text prompt studio and alt text - User authentication with JWT/cookies - Admin panel with voice management - Job queue with Celery - PostgreSQL + Redis backend - Next.js 15 + FastAPI architecture 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
133 lines
3.9 KiB
Python
133 lines
3.9 KiB
Python
"""Job API Routes"""
|
|
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional
|
|
from uuid import UUID
|
|
from datetime import datetime
|
|
|
|
from app.database import get_db
|
|
from app.models.job import Job
|
|
from app.models.user import User
|
|
from app.schemas.job import JobCreate, JobResponse, JobUpdate
|
|
from app.services.job_processor import process_job
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/")
|
|
def get_jobs(
|
|
page: int = 1,
|
|
limit: int = 50,
|
|
status: Optional[str] = None,
|
|
module: Optional[str] = None,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get all jobs with optional filtering and pagination"""
|
|
query = db.query(Job)
|
|
|
|
if status:
|
|
query = query.filter(Job.status == status)
|
|
if module:
|
|
query = query.filter(Job.module == module)
|
|
|
|
# Get total count
|
|
total = query.count()
|
|
|
|
# Calculate offset from page
|
|
skip = (page - 1) * limit
|
|
|
|
jobs = query.order_by(Job.created_at.desc()).offset(skip).limit(limit).all()
|
|
|
|
return {
|
|
"items": [
|
|
{
|
|
"id": str(job.id),
|
|
"module": job.module,
|
|
"action": job.action,
|
|
"status": job.status,
|
|
"progress": job.progress or 0,
|
|
"input_data": job.input_data,
|
|
"output_data": job.output_data,
|
|
"input_asset_ids": [str(a) for a in job.input_asset_ids] if job.input_asset_ids else None,
|
|
"output_asset_ids": [str(a) for a in job.output_asset_ids] if job.output_asset_ids else None,
|
|
"error_message": job.error_message,
|
|
"api_provider": job.api_provider,
|
|
"api_model": job.api_model,
|
|
"created_at": job.created_at.isoformat() if job.created_at else None,
|
|
"completed_at": job.completed_at.isoformat() if job.completed_at else None,
|
|
}
|
|
for job in jobs
|
|
],
|
|
"total": total,
|
|
"page": page,
|
|
"limit": limit
|
|
}
|
|
|
|
|
|
@router.get("/{job_id}", response_model=JobResponse)
|
|
def get_job(job_id: UUID, db: Session = Depends(get_db)):
|
|
"""Get job by ID"""
|
|
job = db.query(Job).filter(Job.id == job_id).first()
|
|
if not job:
|
|
raise HTTPException(status_code=404, detail="Job not found")
|
|
return job
|
|
|
|
|
|
@router.post("/", response_model=JobResponse)
|
|
def create_job(
|
|
job: JobCreate,
|
|
background_tasks: BackgroundTasks,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Create a new job and queue it for processing"""
|
|
# Get test user if no user_id provided
|
|
if not job.user_id:
|
|
user = db.query(User).filter(User.email == "test@forge.ai").first()
|
|
if user:
|
|
job.user_id = user.id
|
|
|
|
# Create job
|
|
db_job = Job(
|
|
**job.model_dump(),
|
|
status="queued",
|
|
queued_at=datetime.utcnow()
|
|
)
|
|
db.add(db_job)
|
|
db.commit()
|
|
db.refresh(db_job)
|
|
|
|
# Queue for background processing
|
|
background_tasks.add_task(process_job, str(db_job.id))
|
|
|
|
return db_job
|
|
|
|
|
|
@router.patch("/{job_id}", response_model=JobResponse)
|
|
def update_job(job_id: UUID, job: JobUpdate, db: Session = Depends(get_db)):
|
|
"""Update a job"""
|
|
db_job = db.query(Job).filter(Job.id == job_id).first()
|
|
if not db_job:
|
|
raise HTTPException(status_code=404, detail="Job not found")
|
|
|
|
for key, value in job.model_dump(exclude_unset=True).items():
|
|
setattr(db_job, key, value)
|
|
|
|
db.commit()
|
|
db.refresh(db_job)
|
|
return db_job
|
|
|
|
|
|
@router.delete("/{job_id}")
|
|
def cancel_job(job_id: UUID, db: Session = Depends(get_db)):
|
|
"""Cancel a job"""
|
|
db_job = db.query(Job).filter(Job.id == job_id).first()
|
|
if not db_job:
|
|
raise HTTPException(status_code=404, detail="Job not found")
|
|
|
|
if db_job.status in ["completed", "failed"]:
|
|
raise HTTPException(status_code=400, detail="Cannot cancel completed or failed job")
|
|
|
|
db_job.status = "cancelled"
|
|
db.commit()
|
|
|
|
return {"message": "Job cancelled"}
|