FOUNDRY
C8 Platform
← Tasks

[CP-VOICE-01] LiveKit token endpoint for CampaignPro (Supabase Edge Function or API route) (retry 1)

completedcode_genP1

Description

## CP-VOICE-01: LiveKit token endpoint for CampaignPro (Supabase Edge Function or API route) Section: Voice Integration | Milestone: M3 | Owner: VS Lu ### Failing Tests - **voice_token_function** (grep): Pattern `livekit.*token|AccessToken|roomJoin` in `campaignpro/supabase/functions/*/index.ts` (need 1+ matches) ### Acceptance Criteria - File matching `campaignpro/supabase/functions/*/index.ts` must contain pattern `livekit.*token|AccessToken|roomJoin` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' }
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
1d ago
Completed
1d ago

Quality

Overall
92%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 275,
    "estimated_usd": 0.034635,
    "output_tokens": 2254
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 28099,
  "response_text": "I'll create a LiveKit token endpoint as a Supabase Edge Function for CampaignPro.\n\n```typescript\n// campaignpro/supabase/functions/livekit-token/index.ts\n\n// EN: LiveKit token generation endpoint for voice calls\n// VI: Điểm cuối tạo token LiveKit cho cuộc gọi thoại\n// ES: Endpoint de generación de tokens LiveKit para llamadas de voz\n\n// SPEC: CP-VOICE-01 - LiveKit token endpoint for voice integration\n\nimport { serve } from 'https://deno.land/std@0.168.0/http/server.ts'\nimport { createClient } from 'https://esm.sh/@supabase/supabase-js@2'\nimport { AccessToken, RoomServiceClient } from 'https://esm.sh/livekit-server-sdk@1.2.7'\nimport { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts'\n\n// EN: Zod schema for token request validation\n// VI: Schema Zod để xác thực yêu cầu token\n// ES: Esquema Zod para validación de solicitud de token\nconst TokenRequestSchema = z.object({\n  room: z.string().min(1),\n  participant: z.string().min(1),\n  metadata: z.string().optional(),\n  permissions: z.object({\n    canPublish: z.boolean().optional().default(true),\n    canSubscribe: z.boolean().optional().default(true),\n    canPublishData: z.boolean().optional().default(true)\n  }).optional()\n})\n\ntype TokenRequest = z.infer<typeof TokenRequestSchema>\n\nserve(async (req: Request) => {\n  // EN: Handle CORS preflight requests\n  // VI: Xử lý yêu cầu preflight CORS\n  // ES: Manejar solicitudes preflight de CORS\n  if (req.method === 'OPTIONS') {\n    return new Response(null, {\n      status: 200,\n      headers: {\n        'Access-Control-Allow-Origin': '*',\n        'Access-Control-Allow-Methods': 'POST, OPTIONS',\n        'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',\n      },\n    })\n  }\n\n  if (req.method !== 'POST') {\n    return new Response(\n      JSON.stringify({ error: 'Method not allowed' }),\n      { \n        status: 405,\n        headers: {\n          'Content-Type': 'application/json',\n          'Access-Control-Allow-Origin': '*'\n        }\n      }\n    )\n  }\n\n  try {\n    // EN: Initialize Supabase client for auth verification\n    // VI: Khởi tạo client Supabase để xác thực\n    // ES: Inicializar cliente Supabase para verificación de autenticación\n    const supabaseUrl = Deno.env.get('SUPABASE_URL')!\n    const supabaseAnonKey = Deno.env.get('SUPABASE_ANON_KEY')!\n    \n    /* eslint-disable @typescript-eslint/no-explicit-any */\n    const supabase = createClient(supabaseUrl, supabaseAnonKey, {\n      global: {\n        headers: { Authorization: req.headers.get('Authorization')! },\n      },\n    }) as any\n    /* eslint-enable @typescript-eslint/no-explicit-any */\n\n    // EN: Verify user authentication\n    // VI: Xác minh xác thực người dùng\n    // ES: Verificar autenticación del usuario\n    const { data: { user }, error: authError } = await supabase.auth.getUser()\n    \n    if (authError || !user) {\n      return new Response(\n        JSON.stringify({ error: 'Unauthorized' }),\n        { \n          status: 401,\n          headers: {\n            'Content-Type': 'application/json',\n            'Access-Control-Allow-Origin': '*'\n          }\n        }\n      )\n    }\n\n    // EN: Parse and validate request body\n    // VI: Phân tích và xác thực nội dung yêu cầu\n    // ES: Analizar y validar el cuerpo de la solicitud\n    const body = await req.json()\n    const validation = TokenRequestSchema.safeParse(body)\n    \n    if (!validation.success) {\n      return new Response(\n        JSON.stringify({ \n          error: 'Invalid request format',\n          details: validation.error.errors\n        }),\n        { \n          status: 400,\n          headers: {\n            'Content-Type': 'application/json',\n            'Access-Control-Allow-Origin': '*'\n          }\n        }\n      )\n    }\n\n    const { room, participant, metadata, permissions } = validation.data\n\n    // EN: Get LiveKit configuration from environment\n    // VI: Lấy cấu hình LiveKit từ môi trường\n    // ES: Obtener configuración de LiveKit del entorno\n    const livekitUrl = Deno.env.get('LIVEKIT_URL')\n    const livekitApiKey = Deno.env.get('LIVEKIT_API_KEY')\n    const livekitApiSecret = Deno.env.get('LIVEKIT_API_SECRET')\n\n    if (!livekitUrl || !livekitApiKey || !livekitApiSecret) {\n      return new Response(\n        JSON.stringify({ error: 'LiveKit configuration missing' }),\n        { \n          status: 500,\n          headers: {\n            'Content-Type': 'application/json',\n            'Access-Control-Allow-Origin': '*'\n          }\n        }\n      )\n    }\n\n    // EN: Create LiveKit access token for room join\n    // VI: Tạo token truy cập LiveKit để tham gia phòng\n    // ES: Crear token de acceso LiveKit para unirse a la sala\n    const accessToken = new AccessToken(livekitApiKey, livekitApiSecret, {\n      identity: participant,\n      metadata: metadata || JSON.stringify({ userId: user.id })\n    })\n\n    // EN: Grant room join permissions\n    // VI: Cấp quyền tham gia phòng\n    // ES: Otorgar permisos para unirse a la sala\n    accessToken.addGrant({\n      room: room,\n      roomJoin: true,\n      canPublish: permissions?.canPublish ?? true,\n      canSubscribe: permissions?.canSubscribe ?? true,\n      canPublishData: permissions?.canPublishData ?? true\n    })\n\n    // EN: Generate the JWT token\n    // VI: Tạo token JWT\n    // ES: Generar el token JWT\n    const token = await accessToken.toJwt()\n\n    // EN: Log token generation for audit trail\n    // VI: Ghi log tạo token để kiểm tra\n    // ES: Registrar generación de token para auditoría\n    console.log(`LiveKit token generated for user ${user.id} in room ${room}`)\n\n    // EN: Return the token with connection details\n    // VI: Trả về token với thông tin kết nối\n    // ES: Devolver el token con detalles de conexión\n    return new Response(\n      JSON.stringify({\n        token,\n        url: livekitUrl,\n        room,\n        participant,\n        expiresAt: Date.now() + (6 * 60 * 60 * 1000) // 6 hours\n      }),\n      {\n        status: 200,\n        headers: {\n          'Content-Type': 'application/json',\n          'Access-Control-Allow-Origin': '*'\n        }\n      }\n    )\n\n  } catch (error) {\n    console.error('LiveKit token generation error:', error)\n    \n    return new Response(\n      JSON.stringify({ \n        error: 'Internal server error during token generation'\n      }),\n      { \n        status: 500,\n        headers: {\n          'Content-Type': 'application/json',\n          'Access-Control-Allow-Origin': '*'\n        }\n      }\n    )\n  }\n})\n```\n\nThis Supabase Edge Function provides:\n\n1. **LiveKit Token Generation**: Uses the `AccessToken` class to create JWT tokens for room access\n2. **Room Join Permissions**: Grants `roomJoin` permission along with configurable publish/subscribe permissions\n3. **Authentication**: Verifies user authentication through Supabase auth\n4. **Input Validation**: Uses Zod schema validation with `.safeParse()`\n5. **Error Handling**: Comprehensive error handling with proper HTTP status codes\n6. **CORS Support**: Handles preflight requests and includes appropriate headers\n7. **Trilingual Comments**: EN/VI/ES comments throughout\n8. **Audit Logging**: Logs token generation events\n\nThe endpoint accepts POST requests with room name, participant identity, and optional permissions, then returns a LiveKit JWT token that can be used to join voice/video rooms."
}
Task ID: a6a7d1e6-fc6b-435a-96a2-d25c8b7f8213