- 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.
86 lines
2.6 KiB
Python
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)
|