task-queue-system/app/api.py
2025-12-11 16:54:45 +01:00

70 lines
2.4 KiB
Python

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import Base, engine, SessionLocal
from app.schemas import CreateJobRequest, JobInfo, TaskInfo
from app.services import create_job, get_job_with_tasks
from app.models import Job, Task
from typing import List
from uuid import UUID
import re, idna
app = FastAPI(title="Task Queue API", version="0.3.0")
async def get_session() -> AsyncSession:
async with SessionLocal() as session:
yield session
@app.on_event("startup")
async def on_startup():
async with engine.begin() as conn:
pass
#await conn.run_sync(Base.metadata.create_all)
DOMAIN_RE = re.compile(r"^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z0-9-]{1,63})+$")
def _validate_domain(domain: str) -> str:
try:
_ = idna.encode(domain).decode()
except Exception:
raise HTTPException(status_code=400, detail="Invalid domain (IDNA)")
if not DOMAIN_RE.match(domain):
raise HTTPException(status_code=400, detail="Invalid domain format")
return domain
@app.post("/jobs", response_model=JobInfo)
async def create_job_endpoint(req: CreateJobRequest, session: AsyncSession = Depends(get_session)):
if not req.job_type:
raise HTTPException(status_code=400, detail="job_type is required")
domain = _validate_domain(req.domain.strip().lower())
job = await create_job(session, req.job_type, domain, req.payload)
job = await get_job_with_tasks(session, job.id)
return _job_to_schema(job)
@app.get("/jobs/{job_id}", response_model=JobInfo)
async def get_job_endpoint(job_id: UUID, session: AsyncSession = Depends(get_session)):
job = await get_job_with_tasks(session, job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
return _job_to_schema(job)
def _job_to_schema(job: Job) -> JobInfo:
tasks: List[TaskInfo] = [
TaskInfo(
id=t.id,
name=t.name,
status=t.status.value if hasattr(t.status, "value") else str(t.status),
retries=t.retries,
max_retries=t.max_retries,
result=t.result,
error=t.error,
)
for t in job.tasks
]
return JobInfo(
id=job.id,
job_type=job.job_type,
status=job.status.value if hasattr(job.status, "value") else str(job.status),
domain=job.domain,
payload=job.payload,
tasks=tasks,
)