From 12256c5b9ca91f2275627c14742dac9038133bf6 Mon Sep 17 00:00:00 2001 From: Jeff-Emmett Date: Thu, 13 Feb 2025 20:38:01 +0100 Subject: [PATCH] working video calls --- .env.example | 17 +++----- .gitignore | 1 + src/shapes/VideoChatShapeUtil.tsx | 41 ++++++++++-------- src/vite-env.d.ts | 8 +--- vite.config.ts | 49 +++++++++++---------- worker/worker.ts | 71 ++++++++++--------------------- wrangler.toml | 14 +++++- 7 files changed, 95 insertions(+), 106 deletions(-) diff --git a/.env.example b/.env.example index 6640e05..cdb8123 100644 --- a/.env.example +++ b/.env.example @@ -1,20 +1,13 @@ -# Google API Credentials +# Frontend (VITE) Public Variables VITE_GOOGLE_CLIENT_ID='your_google_client_id' -VITE_GOOGLE_API_KEY='your_google_api_key' VITE_GOOGLE_MAPS_API_KEY='your_google_maps_api_key' +VITE_DAILY_DOMAIN='your_daily_domain' +VITE_TLDRAW_WORKER_URL='your_worker_url' -# Cloudflare Worker +# Worker-only Variables (Do not prefix with VITE_) CLOUDFLARE_API_TOKEN='your_cloudflare_token' CLOUDFLARE_ACCOUNT_ID='your_account_id' CLOUDFLARE_ZONE_ID='your_zone_id' - -# Worker URL -TLDRAW_WORKER_URL='your_worker_url' - -# R2 Bucket Configuration R2_BUCKET_NAME='your_bucket_name' R2_PREVIEW_BUCKET_NAME='your_preview_bucket_name' - -# Daily.co Configuration -VITE_DAILY_API_KEY=your_daily_api_key_here -VITE_DAILY_DOMAIN='your_daily_domain' \ No newline at end of file +DAILY_API_KEY=your_daily_api_key_here \ No newline at end of file diff --git a/.gitignore b/.gitignore index 975e700..16888ad 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ dist .env.local .env.*.local .dev.vars +.env.production diff --git a/src/shapes/VideoChatShapeUtil.tsx b/src/shapes/VideoChatShapeUtil.tsx index 7326c52..621e4d8 100644 --- a/src/shapes/VideoChatShapeUtil.tsx +++ b/src/shapes/VideoChatShapeUtil.tsx @@ -1,6 +1,10 @@ import { BaseBoxShapeUtil, TLBaseShape } from "tldraw" import { useEffect, useState } from "react" +interface DailyApiResponse { + url: string; +} + export type IVideoChatShape = TLBaseShape< "VideoChat", { @@ -36,33 +40,36 @@ export class VideoChatShape extends BaseBoxShapeUtil { } try { - const apiKey = import.meta.env["VITE_DAILY_API_KEY"] - console.log("API Key available:", !!apiKey) - if (!apiKey) throw new Error("Daily API key is missing") + const workerUrl = import.meta.env.VITE_TLDRAW_WORKER_URL + const apiKey = import.meta.env.VITE_DAILY_API_KEY - const response = await fetch("https://api.daily.co/v1/rooms", { - method: "POST", + if (!apiKey) { + throw new Error('Daily.co API key not configured') + } + + const response = await fetch(`${workerUrl}/daily/rooms`, { + method: 'POST', headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${apiKey.trim()}`, + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ properties: { enable_chat: true, - start_audio_off: true, + enable_screenshare: true, start_video_off: true, - }, - }), + start_audio_off: true + } + }) }) - console.log("Response status:", response.status) - console.log("Response data:", await response.clone().json()) + if (!response.ok) { + const error = await response.json() + throw new Error(`Failed to create room (${response.status}): ${JSON.stringify(error)}`) + } - if (!response.ok) - throw new Error(`Failed to create room (${response.status})`) - - const responseData = (await response.json()) as { url: string } - const url = responseData.url + const data = (await response.json()) as DailyApiResponse; + const url = data.url; if (!url) throw new Error("Room URL is missing") diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 83cff2d..499b046 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -3,12 +3,8 @@ interface ImportMetaEnv { readonly VITE_TLDRAW_WORKER_URL: string readonly VITE_GOOGLE_MAPS_API_KEY: string - readonly VITE_DAILY_API_KEY: string - readonly VITE_CLOUDFLARE_API_TOKEN: string - readonly VITE_CLOUDFLARE_ACCOUNT_ID: string - readonly VITE_CLOUDFLARE_ZONE_ID: string - readonly VITE_R2_BUCKET_NAME: string - readonly VITE_R2_PREVIEW_BUCKET_NAME: string + readonly VITE_GOOGLE_CLIENT_ID: string + readonly VITE_DAILY_DOMAIN: string } interface ImportMeta { diff --git a/vite.config.ts b/vite.config.ts index c2facbd..a84e7e1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,26 +1,31 @@ -import { defineConfig } from "vite" +import { defineConfig, loadEnv } from "vite" import react from "@vitejs/plugin-react" -export default defineConfig({ - envPrefix: ["VITE_"], - plugins: [react()], - server: { - host: "0.0.0.0", - port: 5173, - }, - build: { - sourcemap: true, - }, - base: "/", - publicDir: "src/public", - resolve: { - alias: { - "@": "/src", +export default defineConfig(({ mode }) => { + // Load env file based on `mode` in the current working directory. + // Set the third parameter to '' to load all env regardless of the `VITE_` prefix. + const env = loadEnv(mode, process.cwd(), '') + + return { + envPrefix: ["VITE_"], + plugins: [react()], + server: { + host: "0.0.0.0", + port: 5173, }, - }, - define: { - "import.meta.env.VITE_WORKER_URL": JSON.stringify( - process.env.VITE_WORKER_URL, - ), - }, + build: { + sourcemap: true, + }, + base: "/", + publicDir: "src/public", + resolve: { + alias: { + "@": "/src", + }, + }, + define: { + __WORKER_URL__: JSON.stringify(env.VITE_TLDRAW_WORKER_URL), + __DAILY_API_KEY__: JSON.stringify(env.VITE_DAILY_API_KEY) + } + } }) diff --git a/worker/worker.ts b/worker/worker.ts index ee35cc3..c0aeeb5 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -124,59 +124,34 @@ const router = AutoRouter({ }) }) - .post("/daily/rooms", async (request, env) => { - try { - const { name, properties } = (await request.json()) as { - name: string - properties: Record - } + .post("/daily/rooms", async (req) => { + const apiKey = req.headers.get('Authorization')?.split('Bearer ')[1] + + if (!apiKey) { + return new Response(JSON.stringify({ error: 'No API key provided' }), { + status: 401, + headers: { 'Content-Type': 'application/json' } + }) + } - // Create a room using Daily.co API - const dailyResponse = await fetch("https://api.daily.co/v1/rooms", { - method: "POST", + try { + const response = await fetch('https://api.daily.co/v1/rooms', { + method: 'POST', headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${env.DAILY_API_KEY}`, - }, - body: JSON.stringify({ - name, - properties, - }), + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` + } }) - const dailyData = await dailyResponse.json() - - if (!dailyResponse.ok) { - return new Response( - JSON.stringify({ - message: - (dailyData as any).info || "Failed to create Daily.co room", - }), - { - status: 400, - headers: { "Content-Type": "application/json" }, - }, - ) - } - - return new Response( - JSON.stringify({ - url: `https://${env.DAILY_DOMAIN}/${(dailyData as any).name}`, - }), - { - headers: { "Content-Type": "application/json" }, - }, - ) + const data = await response.json() + return new Response(JSON.stringify(data), { + headers: { 'Content-Type': 'application/json' } + }) } catch (error) { - return new Response( - JSON.stringify({ - message: error instanceof Error ? error.message : "Unknown error", - }), - { - status: 500, - headers: { "Content-Type": "application/json" }, - }, - ) + return new Response(JSON.stringify({ error: (error as Error).message }), { + status: 500, + headers: { 'Content-Type': 'application/json' } + }) } }) diff --git a/wrangler.toml b/wrangler.toml index fc7f66f..a5f8322 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -6,6 +6,7 @@ account_id = "0e7b3338d5278ed1b148e6456b940913" [vars] # Environment variables are managed in Cloudflare Dashboard # Workers & Pages → jeffemmett-canvas → Settings → Variables +DAILY_DOMAIN = "mycopunks.daily.co" [dev] port = 5172 @@ -32,10 +33,21 @@ binding = 'BOARD_BACKUPS_BUCKET' bucket_name = 'board-backups' preview_bucket_name = 'board-backups-preview' +[miniflare] +kv_persist = true +r2_persist = true +durable_objects_persist = true + [observability] enabled = true head_sampling_rate = 1 [triggers] crons = ["0 0 * * *"] # Run at midnight UTC every day -# crons = ["*/10 * * * *"] # Run every 10 minutes \ No newline at end of file +# crons = ["*/10 * * * *"] # Run every 10 minutes + +# Secrets should be set using `wrangler secret put` command +# DO NOT put these directly in wrangler.toml: +# - DAILY_API_KEY +# - CLOUDFLARE_API_TOKEN +# etc. \ No newline at end of file