diff --git a/.env.example b/.env.example
index 1629262..370f44e 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,6 @@
# Frontend (VITE) Public Variables
VITE_GOOGLE_CLIENT_ID='your_google_client_id'
VITE_GOOGLE_MAPS_API_KEY='your_google_maps_api_key'
-VITE_DAILY_DOMAIN='your_daily_domain'
VITE_TLDRAW_WORKER_URL='your_worker_url'
# AI Configuration
@@ -25,5 +24,4 @@ CLOUDFLARE_API_TOKEN='your_cloudflare_token'
CLOUDFLARE_ACCOUNT_ID='your_account_id'
CLOUDFLARE_ZONE_ID='your_zone_id'
R2_BUCKET_NAME='your_bucket_name'
-R2_PREVIEW_BUCKET_NAME='your_preview_bucket_name'
-DAILY_API_KEY=your_daily_api_key_here
\ No newline at end of file
+R2_PREVIEW_BUCKET_NAME='your_preview_bucket_name'
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index c88d651..0cb351d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,8 +18,6 @@
"@automerge/automerge-repo-react-hooks": "^2.2.0",
"@automerge/automerge-repo-storage-indexeddb": "^2.5.0",
"@chengsokdara/use-whisper": "^0.2.0",
- "@daily-co/daily-js": "^0.60.0",
- "@daily-co/daily-react": "^0.20.0",
"@fal-ai/client": "^1.7.2",
"@mdxeditor/editor": "^3.51.0",
"@noble/hashes": "^2.0.1",
@@ -3411,40 +3409,6 @@
"node": ">=18"
}
},
- "node_modules/@daily-co/daily-js": {
- "version": "0.60.0",
- "resolved": "https://registry.npmjs.org/@daily-co/daily-js/-/daily-js-0.60.0.tgz",
- "integrity": "sha512-4GkmOKbxZfen4DI6N1HVqj5CiWrg7r8xALgcbwb5V+Ij1h7LHODDDd78XqhzEBBQp4yNjg6U2wz+l/cVznqc4A==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "@babel/runtime": "^7.12.5",
- "@sentry/browser": "^7.60.1",
- "bowser": "^2.8.1",
- "dequal": "^2.0.3",
- "events": "^3.1.0"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/@daily-co/daily-react": {
- "version": "0.20.0",
- "resolved": "https://registry.npmjs.org/@daily-co/daily-react/-/daily-react-0.20.0.tgz",
- "integrity": "sha512-Agcp5+nvMtZfej2jzPyl8ExmXG1Kk4ULk6BHz24RvNHYVsri8eJ3NSWZgJBmqPtCuEjLjM3wdo7/e1Aew0sfoA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "fast-deep-equal": "^3.1.3",
- "lodash.throttle": "^4.1.1"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "@daily-co/daily-js": ">=0.68.0 <1",
- "react": ">=16.13.1",
- "recoil": "^0.7.0"
- }
- },
"node_modules/@dimforge/rapier3d-compat": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
@@ -8025,132 +7989,6 @@
"url": "https://paulmillr.com/funding/"
}
},
- "node_modules/@sentry-internal/feedback": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.120.4.tgz",
- "integrity": "sha512-eSwgvTdrh03zYYaI6UVOjI9p4VmKg6+c2+CBQfRZX++6wwnCVsNv7XF7WUIpVGBAkJ0N2oapjQmCzJKGKBRWQg==",
- "license": "MIT",
- "dependencies": {
- "@sentry/core": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@sentry-internal/replay-canvas": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.120.4.tgz",
- "integrity": "sha512-2+W4CgUL1VzrPjArbTid4WhKh7HH21vREVilZdvffQPVwOEpgNTPAb69loQuTlhJVveh9hWTj2nE5UXLbLP+AA==",
- "license": "MIT",
- "dependencies": {
- "@sentry/core": "7.120.4",
- "@sentry/replay": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@sentry-internal/tracing": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz",
- "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==",
- "license": "MIT",
- "dependencies": {
- "@sentry/core": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@sentry/browser": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.120.4.tgz",
- "integrity": "sha512-ymlNtIPG6HAKzM/JXpWVGCzCNufZNADfy+O/olZuVJW5Be1DtOFyRnBvz0LeKbmxJbXb2lX/XMhuen6PXPdoQw==",
- "license": "MIT",
- "dependencies": {
- "@sentry-internal/feedback": "7.120.4",
- "@sentry-internal/replay-canvas": "7.120.4",
- "@sentry-internal/tracing": "7.120.4",
- "@sentry/core": "7.120.4",
- "@sentry/integrations": "7.120.4",
- "@sentry/replay": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@sentry/core": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz",
- "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==",
- "license": "MIT",
- "dependencies": {
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@sentry/integrations": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz",
- "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==",
- "license": "MIT",
- "dependencies": {
- "@sentry/core": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4",
- "localforage": "^1.8.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@sentry/replay": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.120.4.tgz",
- "integrity": "sha512-FW8sPenNFfnO/K7sncsSTX4rIVak9j7VUiLIagJrcqZIC7d1dInFNjy8CdVJUlyz3Y3TOgIl3L3+ZpjfyMnaZg==",
- "license": "MIT",
- "dependencies": {
- "@sentry-internal/tracing": "7.120.4",
- "@sentry/core": "7.120.4",
- "@sentry/types": "7.120.4",
- "@sentry/utils": "7.120.4"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@sentry/types": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz",
- "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@sentry/utils": {
- "version": "7.120.4",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz",
- "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==",
- "license": "MIT",
- "dependencies": {
- "@sentry/types": "7.120.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/@sindresorhus/is": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.1.tgz",
@@ -11917,12 +11755,6 @@
"base-x": "^3.0.2"
}
},
- "node_modules/bowser": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
- "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
- "license": "MIT"
- },
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -17241,15 +17073,6 @@
"@types/trusted-types": "^2.0.2"
}
},
- "node_modules/localforage": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
- "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
- "license": "Apache-2.0",
- "dependencies": {
- "lie": "3.1.1"
- }
- },
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
diff --git a/package.json b/package.json
index 7854702..ac2ed7c 100644
--- a/package.json
+++ b/package.json
@@ -44,8 +44,6 @@
"@automerge/automerge-repo-react-hooks": "^2.2.0",
"@automerge/automerge-repo-storage-indexeddb": "^2.5.0",
"@chengsokdara/use-whisper": "^0.2.0",
- "@daily-co/daily-js": "^0.60.0",
- "@daily-co/daily-react": "^0.20.0",
"@fal-ai/client": "^1.7.2",
"@mdxeditor/editor": "^3.51.0",
"@noble/hashes": "^2.0.1",
diff --git a/src/App.tsx b/src/App.tsx
index 2622786..2a60a0e 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -34,11 +34,6 @@ import { Web3Provider } from './providers/Web3Provider';
// Import Google Data test component
import { GoogleDataTest } from './components/GoogleDataTest';
-// Lazy load Daily.co provider - only needed for video chat
-const DailyProvider = lazy(() =>
- import('@daily-co/daily-react').then(m => ({ default: m.DailyProvider }))
-);
-
// Loading skeleton for lazy-loaded routes
const LoadingSpinner = () => (
(
);
-// Daily.co call object - initialized lazily when needed
-let dailyCallObject: any = null;
-const getDailyCallObject = async () => {
- if (dailyCallObject) return dailyCallObject;
-
- try {
- // Only create call object if we're in a secure context and mediaDevices is available
- if (typeof window !== 'undefined' &&
- window.location.protocol === 'https:' &&
- navigator.mediaDevices) {
- const Daily = (await import('@daily-co/daily-js')).default;
- dailyCallObject = Daily.createCallObject();
- }
- } catch (error) {
- console.warn('Daily.co call object initialization failed:', error);
- }
- return dailyCallObject;
-};
-
/**
* Optional Auth Route component
* Allows guests to browse, but provides login option
@@ -151,8 +127,7 @@ const AppWithProviders = () => {
}>
-
-
+
{/* Display notifications */}
@@ -227,8 +202,7 @@ const AppWithProviders = () => {
} />
-
-
+
diff --git a/src/automerge/useAutomergeSyncRepo.ts b/src/automerge/useAutomergeSyncRepo.ts
index c123aa3..e18c67a 100644
--- a/src/automerge/useAutomergeSyncRepo.ts
+++ b/src/automerge/useAutomergeSyncRepo.ts
@@ -481,11 +481,11 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
const localRecordCount = localDoc?.store ? Object.keys(localDoc.store).length : 0
// Merge server data with local data
- // Strategy:
- // 1. If local has NO SHAPES (only ephemeral records), use server data
- // 2. If server has SIGNIFICANTLY MORE shapes (10x), prefer server (stale local cache)
- // 3. Otherwise, only add server records that don't exist locally
- // (preserve offline changes, let Automerge CRDT sync handle conflicts)
+ // Strategy (IMPROVED):
+ // 1. Server is the source of truth for initial page load
+ // 2. Always update local with server data for shape records
+ // 3. Keep local-only records (potential offline additions not yet synced)
+ // 4. This ensures stale IndexedDB cache doesn't override server data
if (serverDoc.store && serverRecordCount > 0) {
// Track if we merged any data (needed outside the change callback)
let totalMerged = 0
@@ -500,46 +500,39 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
const localShapeCount = Object.values(doc.store).filter((r: any) => r?.typeName === 'shape').length
const localIsEmpty = Object.keys(doc.store).length === 0
- // Server has significantly more shapes - local is likely stale cache
- // Use 10x threshold or server has shapes but local has none
- const serverHasSignificantlyMore = (
- localShapeCount === 0 && serverShapeCount > 0
- ) || (
- serverShapeCount > 0 && localShapeCount > 0 && serverShapeCount >= localShapeCount * 10
- )
-
- // If local has no shapes but server does, or server has 10x more,
- // replace local with server data (but keep local ephemeral records)
- const shouldPreferServer = localIsEmpty || localShapeCount === 0 || serverHasSignificantlyMore
+ // IMPROVED: Server is source of truth on initial load
+ // Prefer server if:
+ // - Local is empty (first load or cleared cache)
+ // - Server has more shapes (local is likely stale/incomplete)
+ // - Local has shapes but server has different/more content
+ const serverHasMoreContent = serverShapeCount > localShapeCount
+ const shouldPreferServer = localIsEmpty || localShapeCount === 0 || serverHasMoreContent
let addedFromServer = 0
- let skippedExisting = 0
- let replacedFromServer = 0
+ let updatedFromServer = 0
+ let keptLocal = 0
Object.entries(serverDoc.store).forEach(([id, record]) => {
- if (shouldPreferServer) {
- // Prefer server data - bootstrap or replace stale local
- if (doc.store[id]) {
- replacedFromServer++
- } else {
- addedFromServer++
- }
- doc.store[id] = record
- } else if (!doc.store[id]) {
- // Local has data but missing this record - add from server
- // This handles: shapes created on another device and synced to R2
+ const existsLocally = !!doc.store[id]
+
+ if (!existsLocally) {
+ // Record doesn't exist locally - add from server
doc.store[id] = record
addedFromServer++
+ } else if (shouldPreferServer) {
+ // Record exists locally but server has more content - update with server version
+ // This handles stale IndexedDB cache scenarios
+ doc.store[id] = record
+ updatedFromServer++
} else {
- // Record exists locally - preserve local version
- // The Automerge binary sync will handle merging conflicts via CRDT
- // This preserves offline edits to existing shapes
- skippedExisting++
+ // Local has equal or more content - keep local version
+ // Local changes will sync to server via normal CRDT mechanism
+ keptLocal++
}
})
- totalMerged = addedFromServer + replacedFromServer
- console.log(`🔄 Server sync: added=${addedFromServer}, replaced=${replacedFromServer}, skipped=${skippedExisting}, shouldPreferServer=${shouldPreferServer}`)
+ totalMerged = addedFromServer + updatedFromServer
+ console.log(`🔄 Server sync: added=${addedFromServer}, updated=${updatedFromServer}, keptLocal=${keptLocal}, serverShapes=${serverShapeCount}, localShapes=${localShapeCount}, preferServer=${shouldPreferServer}`)
})
const finalDoc = handle.doc()
diff --git a/worker/worker.ts b/worker/worker.ts
index 436b9e0..e5e801d 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -322,388 +322,6 @@ const router = AutoRouter({
})
})
- .post("/daily/rooms", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- // Get the request body from the client
- const body = await req.json()
-
- const response = await fetch('https://api.daily.co/v1/rooms', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- },
- body: JSON.stringify(body)
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- // Get room info by name
- .get("/daily/rooms/:roomName", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { roomName } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/rooms/${roomName}`, {
- method: 'GET',
- headers: {
- 'Authorization': `Bearer ${apiKey}`,
- 'Content-Type': 'application/json'
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- .post("/daily/tokens", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const body = await req.json() as { room_name: string; properties: any };
- const response = await fetch('https://api.daily.co/v1/meeting-tokens', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- },
- body: JSON.stringify({
- room_name: body.room_name,
- properties: body.properties
- })
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- // Add new transcription endpoints
- .post("/daily/rooms/:roomName/start-transcription", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { roomName } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/rooms/${roomName}/transcription/start`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- .post("/daily/rooms/:roomName/stop-transcription", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { roomName } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/rooms/${roomName}/transcription/stop`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- // Add endpoint to get transcript access link
- .get("/daily/transcript/:transcriptId/access-link", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { transcriptId } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/transcript/${transcriptId}/access-link`, {
- method: 'GET',
- headers: {
- 'Authorization': `Bearer ${apiKey}`,
- 'Content-Type': 'application/json'
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- // Add endpoint to get transcript text
- .get("/daily/transcript/:transcriptId", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { transcriptId } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/transcripts/${transcriptId}`, {
- method: 'GET',
- headers: {
- 'Authorization': `Bearer ${apiKey}`,
- 'Content-Type': 'application/json'
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- // Recording endpoints
- .post("/daily/recordings/start", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const body = await req.json() as any;
- const response = await fetch('https://api.daily.co/v1/recordings', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- },
- body: JSON.stringify(body)
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
- .post("/daily/recordings/:recordingId/stop", async (req, env) => {
- // Use server-side API key - never expose to client
- const apiKey = env.DAILY_API_KEY
- const { recordingId } = req.params
-
- if (!apiKey) {
- return new Response(JSON.stringify({ error: 'Daily.co API key not configured on server' }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
-
- try {
- const response = await fetch(`https://api.daily.co/v1/recordings/${recordingId}/stop`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey}`
- }
- })
-
- if (!response.ok) {
- const error = await response.json()
- return new Response(JSON.stringify(error), {
- status: response.status,
- 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({ error: (error as Error).message }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' }
- })
- }
- })
-
// Fathom API endpoints (api.fathom.ai)
.get("/fathom/meetings", async (req) => {
console.log('Fathom meetings endpoint called')
diff --git a/wrangler.dev.toml b/wrangler.dev.toml
index cd38cc4..0243659 100644
--- a/wrangler.dev.toml
+++ b/wrangler.dev.toml
@@ -5,7 +5,6 @@ account_id = "0e7b3338d5278ed1b148e6456b940913"
[vars]
# Development environment variables
-DAILY_DOMAIN = "mycopunks.daily.co"
[dev]
port = 5172
@@ -52,6 +51,7 @@ crons = ["0 0 * * *"] # Run at midnight UTC every day
# Secrets should be set using `wrangler secret put` command for dev environment
# DO NOT put these directly in wrangler.toml:
-# - DAILY_API_KEY
# - CLOUDFLARE_API_TOKEN
+# - FAL_API_KEY
+# - RUNPOD_API_KEY
# etc.
diff --git a/wrangler.toml b/wrangler.toml
index ab7f3e7..06cedde 100644
--- a/wrangler.toml
+++ b/wrangler.toml
@@ -10,7 +10,6 @@ account_id = "0e7b3338d5278ed1b148e6456b940913"
[vars]
# Environment variables are managed in Cloudflare Dashboard
# Workers & Pages → jeffemmett-canvas → Settings → Variables
-DAILY_DOMAIN = "mycopunks.daily.co"
# RunPod AI Service Configuration (defaults hardcoded in src/lib/clientConfig.ts)
# These are documented here for reference - actual values are in the client code
@@ -77,7 +76,7 @@ name = "jeffemmett-canvas-automerge-dev"
compatibility_date = "2024-07-01"
[env.dev.vars]
-DAILY_DOMAIN = "mycopunks.daily.co"
+# Dev environment variables
[env.dev.durable_objects]
bindings = [
@@ -106,7 +105,6 @@ crons = ["0 0 * * *"] # Run at midnight UTC every day
# Secrets should be set using `wrangler secret put` command
# DO NOT put these directly in wrangler.toml:
-# - DAILY_API_KEY
# - CLOUDFLARE_API_TOKEN
# - FAL_API_KEY # For fal.ai image/video generation proxy
# - RUNPOD_API_KEY # For RunPod AI endpoints proxy