mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
101 lines
3.0 KiB
Python
101 lines
3.0 KiB
Python
"""
|
|
Mint LiveKit participant JWTs for DM calls. Requires LIVEKIT_API_KEY, LIVEKIT_API_SECRET,
|
|
and LIVEKIT_URL (WebSocket URL for clients, e.g. wss://livekit.example.com or ws://host:7880).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import uuid
|
|
from datetime import timedelta
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..dependencies import get_current_user, get_db
|
|
from ..models import User
|
|
|
|
logger = logging.getLogger("uvicorn.error")
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class LiveKitTokenRequest(BaseModel):
|
|
peer_user_id: int = Field(..., description="The other participant (DM peer)")
|
|
room_name: str | None = Field(
|
|
None,
|
|
description="Existing room from an invite; omit to create a new room",
|
|
)
|
|
|
|
|
|
class LiveKitTokenResponse(BaseModel):
|
|
server_url: str
|
|
token: str
|
|
room_name: str
|
|
|
|
|
|
def _livekit_env() -> tuple[str, str, str]:
|
|
api_key = os.getenv("LIVEKIT_API_KEY", "").strip()
|
|
api_secret = os.getenv("LIVEKIT_API_SECRET", "").strip()
|
|
server_url = os.getenv("LIVEKIT_URL", "").strip()
|
|
if not api_key or not api_secret or not server_url:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="LiveKit is not configured (LIVEKIT_API_KEY / LIVEKIT_API_SECRET / LIVEKIT_URL)",
|
|
)
|
|
return api_key, api_secret, server_url
|
|
|
|
|
|
@router.post("/token", response_model=LiveKitTokenResponse)
|
|
async def create_livekit_token(
|
|
body: LiveKitTokenRequest,
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""
|
|
Issue a short-lived JWT for joining a 1:1 call room with peer_user_id.
|
|
"""
|
|
if body.peer_user_id == user.id:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="peer_user_id must differ from caller")
|
|
|
|
peer = db.query(User).filter(User.id == body.peer_user_id).first()
|
|
if not peer:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Peer user not found")
|
|
|
|
api_key, api_secret, server_url = _livekit_env()
|
|
|
|
if body.room_name and body.room_name.strip():
|
|
room_name = body.room_name.strip()
|
|
else:
|
|
room_name = f"call-{uuid.uuid4().hex}"
|
|
|
|
try:
|
|
from livekit.api import AccessToken, VideoGrants
|
|
except ImportError as e:
|
|
logger.exception("livekit-api not installed")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
detail="LiveKit SDK unavailable on server",
|
|
) from e
|
|
|
|
grants = VideoGrants(
|
|
room_join=True,
|
|
room=room_name,
|
|
can_publish=True,
|
|
can_subscribe=True,
|
|
can_publish_data=True,
|
|
)
|
|
|
|
token = (
|
|
AccessToken(api_key, api_secret)
|
|
.with_identity(str(user.id))
|
|
.with_name(user.username or str(user.id))
|
|
.with_ttl(timedelta(hours=1))
|
|
.with_grants(grants)
|
|
)
|
|
|
|
jwt_token = token.to_jwt()
|
|
|
|
return LiveKitTokenResponse(server_url=server_url, token=jwt_token, room_name=room_name)
|