annator-command-center / api /channel_routes.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 2)
a10e62e verified
Raw
History Blame Contribute Delete
9.37 kB
"""
Channel Routes - REST API for channel management.
OpenClaw Integration: Context-specific conversations (project, support, engineering, general).
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy.orm import Session
from typing import List, Optional
from core.models import Channel, get_db
router = APIRouter(prefix="/api/channels", tags=["Channels"])
class CreateChannelRequest(BaseModel):
name: str # project-xyz, support, engineering
display_name: str # "Project XYZ", "Support", "Engineering"
description: Optional[str] = None
channel_type: str # project, support, engineering, general
is_public: bool = True
created_by: str # user_id
agent_members: List[str] = []
user_members: List[str] = []
class UpdateChannelRequest(BaseModel):
display_name: Optional[str] = None
description: Optional[str] = None
is_public: Optional[bool] = None
agent_members: Optional[List[str]] = None
user_members: Optional[List[str]] = None
class ChannelResponse(BaseModel):
id: str
name: str
display_name: str
description: Optional[str]
channel_type: str
is_public: bool
created_by: str
agent_members: List[str]
user_members: List[str]
created_at: str
@router.post("/", response_model=ChannelResponse)
async def create_channel(
request: CreateChannelRequest,
db: Session = Depends(get_db)
):
"""
Create new channel.
**Channel Types:**
- project: Project-specific discussions
- support: Customer support coordination
- engineering: Technical discussions
- general: Default public channel
**Access Control:**
- Humans can create channels
- is_public=false for private channels
- agent_members and user_members control access
"""
# Check if channel name already exists
existing = db.query(Channel).filter(Channel.name == request.name).first()
if existing:
raise HTTPException(status_code=400, detail=f"Channel {request.name} already exists")
# Validate channel_type
valid_types = ["project", "support", "engineering", "general"]
if request.channel_type not in valid_types:
raise HTTPException(
status_code=400,
detail=f"Invalid channel_type '{request.channel_type}'. Must be one of: {', '.join(valid_types)}"
)
channel = Channel(
name=request.name,
display_name=request.display_name,
description=request.description,
channel_type=request.channel_type,
is_public=request.is_public,
created_by=request.created_by,
agent_members=request.agent_members,
user_members=request.user_members
)
db.add(channel)
db.commit()
db.refresh(channel)
return ChannelResponse(
id=channel.id,
name=channel.name,
display_name=channel.display_name,
description=channel.description,
channel_type=channel.channel_type,
is_public=channel.is_public,
created_by=channel.created_by,
agent_members=channel.agent_members,
user_members=channel.user_members,
created_at=channel.created_at.isoformat()
)
@router.get("/", response_model=List[ChannelResponse])
async def list_channels(
channel_type: Optional[str] = None,
is_public: Optional[bool] = None,
limit: int = Query(50, le=100),
offset: int = Query(0, ge=0),
db: Session = Depends(get_db)
):
"""
List all channels.
**Filters:**
- channel_type: Filter by type (project, support, engineering, general)
- is_public: Filter by public/private
- Pagination: limit + offset
"""
query = db.query(Channel)
if channel_type:
query = query.filter(Channel.channel_type == channel_type)
if is_public is not None:
query = query.filter(Channel.is_public == is_public)
channels = query.order_by(Channel.created_at.desc()).offset(offset).limit(limit).all()
return [
ChannelResponse(
id=c.id,
name=c.name,
display_name=c.display_name,
description=c.description,
channel_type=c.channel_type,
is_public=c.is_public,
created_by=c.created_by,
agent_members=c.agent_members,
user_members=c.user_members,
created_at=c.created_at.isoformat()
)
for c in channels
]
@router.get("/{channel_id}", response_model=ChannelResponse)
async def get_channel(
channel_id: str,
db: Session = Depends(get_db)
):
"""
Get channel by ID.
"""
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if not channel:
raise HTTPException(status_code=404, detail=f"Channel {channel_id} not found")
return ChannelResponse(
id=channel.id,
name=channel.name,
display_name=channel.display_name,
description=channel.description,
channel_type=channel.channel_type,
is_public=channel.is_public,
created_by=channel.created_by,
agent_members=channel.agent_members,
user_members=channel.user_members,
created_at=channel.created_at.isoformat()
)
@router.put("/{channel_id}", response_model=ChannelResponse)
async def update_channel(
channel_id: str,
request: UpdateChannelRequest,
db: Session = Depends(get_db)
):
"""
Update channel.
Only update fields that are provided.
"""
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if not channel:
raise HTTPException(status_code=404, detail=f"Channel {channel_id} not found")
if request.display_name is not None:
channel.display_name = request.display_name
if request.description is not None:
channel.description = request.description
if request.is_public is not None:
channel.is_public = request.is_public
if request.agent_members is not None:
channel.agent_members = request.agent_members
if request.user_members is not None:
channel.user_members = request.user_members
db.commit()
db.refresh(channel)
return ChannelResponse(
id=channel.id,
name=channel.name,
display_name=channel.display_name,
description=channel.description,
channel_type=channel.channel_type,
is_public=channel.is_public,
created_by=channel.created_by,
agent_members=channel.agent_members,
user_members=channel.user_members,
created_at=channel.created_at.isoformat()
)
@router.delete("/{channel_id}")
async def delete_channel(
channel_id: str,
db: Session = Depends(get_db)
):
"""
Delete channel.
**Warning:** This will also delete all posts in this channel.
"""
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if not channel:
raise HTTPException(status_code=404, detail=f"Channel {channel_id} not found")
db.delete(channel)
db.commit()
return {"message": f"Channel {channel_id} deleted"}
@router.post("/{channel_id}/members")
async def add_channel_member(
channel_id: str,
member_type: str, # "agent" or "user"
member_id: str,
db: Session = Depends(get_db)
):
"""
Add member to channel.
** member_type: "agent" or "user"
** member_id: agent_id or user_id
"""
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if not channel:
raise HTTPException(status_code=404, detail=f"Channel {channel_id} not found")
if member_type == "agent":
if member_id not in channel.agent_members:
channel.agent_members.append(member_id)
elif member_type == "user":
if member_id not in channel.user_members:
channel.user_members.append(member_id)
else:
raise HTTPException(status_code=400, detail=f"Invalid member_type '{member_type}'. Must be 'agent' or 'user'")
db.commit()
db.refresh(channel)
return {
"message": f"Added {member_type} {member_id} to channel {channel_id}",
"agent_members": channel.agent_members,
"user_members": channel.user_members
}
@router.delete("/{channel_id}/members")
async def remove_channel_member(
channel_id: str,
member_type: str, # "agent" or "user"
member_id: str,
db: Session = Depends(get_db)
):
"""
Remove member from channel.
** member_type: "agent" or "user"
** member_id: agent_id or user_id
"""
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if not channel:
raise HTTPException(status_code=404, detail=f"Channel {channel_id} not found")
if member_type == "agent":
if member_id in channel.agent_members:
channel.agent_members.remove(member_id)
elif member_type == "user":
if member_id in channel.user_members:
channel.user_members.remove(member_id)
else:
raise HTTPException(status_code=400, detail=f"Invalid member_type '{member_type}'. Must be 'agent' or 'user'")
db.commit()
db.refresh(channel)
return {
"message": f"Removed {member_type} {member_id} from channel {channel_id}",
"agent_members": channel.agent_members,
"user_members": channel.user_members
}