Files
ai-video-fullstack/backend/routes/realtime_api_keys.py
Xin Wang 86639692ba feat: implement OpenAI-compatible Realtime API with authentication and management features
- Added support for public Realtime API, including new routes for managing API keys and handling WebRTC connections.
- Introduced RealtimeApiKey model and associated CRUD operations for admin management of API keys.
- Implemented authentication mechanisms for API keys and client secrets.
- Enhanced environment configuration with new secrets for Realtime API.
- Created OpenAIRealtime session management and event processing for real-time interactions.
- Updated schemas and settings to accommodate new features and ensure compatibility with existing systems.
2026-08-11 10:05:55 +08:00

86 lines
2.6 KiB
Python

"""Admin-only management of public Realtime API credentials."""
from __future__ import annotations
from datetime import UTC, datetime
from db.models import RealtimeApiKey
from db.session import get_session
from fastapi import APIRouter, Depends, HTTPException, Response, status
from schemas import RealtimeApiKeyCreate, RealtimeApiKeyCreated, RealtimeApiKeyOut
from services.auth import require_admin
from services.openai_realtime.auth import create_api_key_value
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
router = APIRouter(
prefix="/api/realtime/api-keys",
tags=["realtime-api-keys"],
dependencies=[Depends(require_admin)],
)
def _status(row: RealtimeApiKey) -> str:
if row.revoked_at is not None:
return "revoked"
if row.expires_at is not None and row.expires_at <= datetime.now(UTC):
return "expired"
return "active"
def _out(row: RealtimeApiKey) -> RealtimeApiKeyOut:
return RealtimeApiKeyOut(
id=row.id,
name=row.name,
key_prefix=row.key_prefix,
status=_status(row),
expires_at=row.expires_at,
last_used_at=row.last_used_at,
revoked_at=row.revoked_at,
created_at=row.created_at,
)
@router.post("", response_model=RealtimeApiKeyCreated, status_code=status.HTTP_201_CREATED)
async def create_realtime_api_key(
body: RealtimeApiKeyCreate,
session: AsyncSession = Depends(get_session),
):
key_id, key, prefix, digest = create_api_key_value()
row = RealtimeApiKey(
id=key_id,
name=body.name,
key_prefix=prefix,
key_hash=digest,
expires_at=body.expires_at,
)
session.add(row)
await session.commit()
await session.refresh(row)
return RealtimeApiKeyCreated(**_out(row).model_dump(), key=key)
@router.get("", response_model=list[RealtimeApiKeyOut])
async def list_realtime_api_keys(session: AsyncSession = Depends(get_session)):
rows = (
await session.execute(
select(RealtimeApiKey).order_by(RealtimeApiKey.created_at.desc())
)
).scalars().all()
return [_out(row) for row in rows]
@router.delete("/{key_id}", status_code=status.HTTP_204_NO_CONTENT)
async def revoke_realtime_api_key(
key_id: str,
session: AsyncSession = Depends(get_session),
):
row = await session.get(RealtimeApiKey, key_id)
if row is None:
raise HTTPException(404, "Realtime API Key 不存在")
if row.revoked_at is None:
row.revoked_at = datetime.now(UTC)
await session.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)