Add Profile/Channel entities with CRUD and remove legacy parser builder.
Introduce reusable ParserProfile and ParseChannel, pair jobs with enqueue flatten, Events admin CRUD, and drop inline/legacy parser-builder UI and aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Admin CRUD for parse channels (Telegram handles in MVP)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import ParseChannel, ParseJob
|
||||
from ..schemas import ParseChannelCreate, ParseChannelRead, ParseChannelUpdate
|
||||
|
||||
router = APIRouter(prefix="/admin/parse-channels", tags=["parse-channels"])
|
||||
|
||||
ALLOWED_SOURCE_TYPES = {"telegram"}
|
||||
|
||||
|
||||
@router.get("", response_model=list[ParseChannelRead])
|
||||
def list_channels(db: Session = Depends(get_db)):
|
||||
return db.query(ParseChannel).order_by(ParseChannel.id.desc()).all()
|
||||
|
||||
|
||||
@router.post("", response_model=ParseChannelRead, status_code=201)
|
||||
def create_channel(payload: ParseChannelCreate, db: Session = Depends(get_db)):
|
||||
source_type = (payload.source_type or "telegram").strip()
|
||||
if source_type not in ALLOWED_SOURCE_TYPES:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"source_type must be one of: {', '.join(sorted(ALLOWED_SOURCE_TYPES))}",
|
||||
)
|
||||
channel = ParseChannel(
|
||||
name=payload.name.strip(),
|
||||
source_type=source_type,
|
||||
channel=payload.channel.strip().lstrip("@"),
|
||||
is_active=payload.is_active,
|
||||
)
|
||||
db.add(channel)
|
||||
db.commit()
|
||||
db.refresh(channel)
|
||||
return channel
|
||||
|
||||
|
||||
@router.get("/{channel_id}", response_model=ParseChannelRead)
|
||||
def get_channel(channel_id: int, db: Session = Depends(get_db)):
|
||||
channel = db.query(ParseChannel).filter(ParseChannel.id == channel_id).first()
|
||||
if not channel:
|
||||
raise HTTPException(status_code=404, detail="Channel not found")
|
||||
return channel
|
||||
|
||||
|
||||
@router.patch("/{channel_id}", response_model=ParseChannelRead)
|
||||
def update_channel(
|
||||
channel_id: int,
|
||||
payload: ParseChannelUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
channel = db.query(ParseChannel).filter(ParseChannel.id == channel_id).first()
|
||||
if not channel:
|
||||
raise HTTPException(status_code=404, detail="Channel not found")
|
||||
|
||||
if payload.name is not None:
|
||||
channel.name = payload.name.strip()
|
||||
if payload.source_type is not None:
|
||||
source_type = payload.source_type.strip()
|
||||
if source_type not in ALLOWED_SOURCE_TYPES:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"source_type must be one of: {', '.join(sorted(ALLOWED_SOURCE_TYPES))}",
|
||||
)
|
||||
channel.source_type = source_type
|
||||
if payload.channel is not None:
|
||||
channel.channel = payload.channel.strip().lstrip("@")
|
||||
if payload.is_active is not None:
|
||||
channel.is_active = payload.is_active
|
||||
|
||||
db.commit()
|
||||
db.refresh(channel)
|
||||
return channel
|
||||
|
||||
|
||||
@router.delete("/{channel_id}", status_code=204)
|
||||
def delete_channel(channel_id: int, db: Session = Depends(get_db)):
|
||||
channel = db.query(ParseChannel).filter(ParseChannel.id == channel_id).first()
|
||||
if not channel:
|
||||
raise HTTPException(status_code=404, detail="Channel not found")
|
||||
|
||||
linked = (
|
||||
db.query(ParseJob)
|
||||
.filter(ParseJob.channel_id == channel_id, ParseJob.status.in_(("queued", "running")))
|
||||
.count()
|
||||
)
|
||||
if linked:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Cannot delete channel used by queued/running jobs",
|
||||
)
|
||||
|
||||
db.delete(channel)
|
||||
db.commit()
|
||||
Reference in New Issue
Block a user