From b700846a9c6f69b8dd7e3be899c9a939bac1cfe2 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 12:52:20 -0500
Subject: [PATCH 01/16] fixing production env
---
package copy.json | 69 -----------------------------------------------
package.json | 14 +++++-----
2 files changed, 7 insertions(+), 76 deletions(-)
delete mode 100644 package copy.json
diff --git a/package copy.json b/package copy.json
deleted file mode 100644
index 0c4f574..0000000
--- a/package copy.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "name": "jeffemmett",
- "version": "1.0.0",
- "description": "Jeff Emmett's personal website",
- "type": "module",
- "scripts": {
- "dev": "concurrently --kill-others --names client,worker --prefix-colors blue,red \"yarn dev:client\" \"yarn dev:worker\"",
- "dev:client": "vite --host --port 5173",
- "dev:worker": "wrangler dev --local --port 5172 --ip 0.0.0.0",
- "build": "tsc && vite build && wrangler deploy",
- "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview",
- "deploy": "yarn build && vercel deploy --prod"
- },
- "keywords": [],
- "author": "Jeff Emmett",
- "license": "ISC",
- "dependencies": {
- "@dimforge/rapier2d": "^0.11.2",
- "@tldraw/assets": "^2.0.0",
- "@tldraw/tldraw": "^3.4.1",
- "@tldraw/sync": "^2.4.6",
- "@tldraw/sync-core": "^2.4.6",
- "@tldraw/tlschema": "^2.4.6",
- "@types/markdown-it": "^14.1.1",
- "@vercel/analytics": "^1.2.2",
- "@whereby.com/browser-sdk": "^3.9.2",
- "cloudflare-workers-unfurl": "^0.0.7",
- "crdts": "^0.2.0",
- "gray-matter": "^4.0.3",
- "itty-router": "^5.0.17",
- "lodash.throttle": "^4.1.1",
- "markdown-it": "^14.1.0",
- "markdown-it-latex2img": "^0.0.6",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-error-boundary": "^4.1.2",
- "react-router-dom": "^6.22.3",
- "tldraw": "^2.4.6",
- "use-local-storage-state": "^19.5.0",
- "vercel": "^39.1.1"
- },
- "devDependencies": {
- "@biomejs/biome": "1.4.1",
- "@cloudflare/types": "^6.29.1",
- "@cloudflare/workers-types": "^4.20240821.1",
- "@types/lodash.throttle": "^4",
- "@types/react": "^18.2.0",
- "@types/react-dom": "^18.2.7",
- "@typescript-eslint/eslint-plugin": "^5.59.0",
- "@typescript-eslint/parser": "^5.59.0",
- "@vitejs/plugin-react": "^4.0.3",
- "@vitejs/plugin-react-swc": "^3.6.0",
- "concurrently": "^8.2.2",
- "eslint": "^8.38.0",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.3.4",
- "typescript": "^5.6.3",
- "vite": "^5.3.3",
- "vite-plugin-static-copy": "^1.0.6",
- "vite-plugin-top-level-await": "^1.3.1",
- "vite-plugin-wasm": "^3.2.2",
- "wrangler": "^3.88.0"
- },
- "resolutions": {
- "react": "^18.2.0",
- "@types/react": "^18.2.0"
- }
-}
\ No newline at end of file
diff --git a/package.json b/package.json
index 4c50064..208b7b6 100644
--- a/package.json
+++ b/package.json
@@ -17,11 +17,11 @@
"license": "ISC",
"dependencies": {
"@dimforge/rapier2d": "^0.11.2",
- "@tldraw/assets": "^3.5.0",
- "@tldraw/sync": "^3.4.1",
- "@tldraw/sync-core": "^3.4.1",
- "@tldraw/tldraw": "^3.4.1",
- "@tldraw/tlschema": "^3.4.1",
+ "@tldraw/assets": "^3.6.0",
+ "@tldraw/sync": "^3.6.0",
+ "@tldraw/sync-core": "^3.6.0",
+ "@tldraw/tldraw": "^3.6.0",
+ "@tldraw/tlschema": "^3.6.0",
"@types/markdown-it": "^14.1.1",
"@vercel/analytics": "^1.2.2",
"cloudflare-workers-unfurl": "^0.0.7",
@@ -35,7 +35,7 @@
"react-dom": "^18.2.0",
"react-error-boundary": "^4.1.2",
"react-router-dom": "^6.22.3",
- "tldraw": "^3.4.1",
+ "tldraw": "^3.6.0",
"use-local-storage-state": "^19.5.0",
"vercel": "^39.1.1"
},
@@ -61,4 +61,4 @@
"vite-plugin-wasm": "^3.2.2",
"wrangler": "^3.88.0"
}
-}
+}
\ No newline at end of file
From 02124ce92011ccf84a5df7ba2a3bb31d8f18c802 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 12:58:25 -0500
Subject: [PATCH 02/16] fix CORS policy
---
worker/TldrawDurableObject.ts | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index 5e4a496..a101989 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -67,15 +67,32 @@ export class TldrawDurableObject {
}
return this.handleConnect(request)
})
- .get('/room/:roomId', async () => {
+ .get('/room/:roomId', async (request) => {
const room = await this.getRoom()
const snapshot = room.getCurrentSnapshot()
- return new Response(JSON.stringify(snapshot.documents))
+ return new Response(JSON.stringify(snapshot.documents), {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': request.headers.get('Origin') || '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type',
+ 'Access-Control-Max-Age': '86400',
+ }
+ })
})
.post('/room/:roomId', async (request) => {
const records = await request.json() as TLRecord[]
const mergedRecords = await this.mergeCrdtState(records)
- return new Response(JSON.stringify(Array.from(mergedRecords)))
+
+ return new Response(JSON.stringify(Array.from(mergedRecords)), {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': request.headers.get('Origin') || '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type',
+ 'Access-Control-Max-Age': '86400',
+ }
+ })
})
// `fetch` is the entry point for all requests to the Durable Object
From 3a2a38c0b6d7a5be7bc8a1122e54230050be976d Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 12:58:46 -0500
Subject: [PATCH 03/16] fix CORS policy
---
worker/worker.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/worker/worker.ts b/worker/worker.ts
index 50c4da9..b99b8ca 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -35,7 +35,10 @@ const { preflight, corsify } = cors({
// 10.*.*.* with any port
/^http:\/\/10\.\d+\.\d+\.\d+:\d+$/,
// Production domain
- /^https:\/\/jeffemmett\.com$/
+ /^https:\/\/jeffemmett\.com$/,
+ /^https:\/\/www\.jeffemmett\.com$/,
+ // Worker domain
+ /^https:\/\/jeffemmett-canvas\.jeffemmett\.workers\.dev$/
]
// Check if origin matches any of our patterns
From 0eb44072190f21cdad1ce65499a86486a7d026ab Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 13:15:38 -0500
Subject: [PATCH 04/16] fix worker deployment
---
.gitignore | 1 +
src/components/Board.tsx | 2 +-
worker/worker.ts | 29 +++++++++++++++++++----------
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/.gitignore b/.gitignore
index 46f7caa..efa4db0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,6 +180,7 @@ dist
# Environment variables
.env*
+.env.development
!.env.example
.vercel
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index b584ae8..9d2f9ee 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -27,7 +27,7 @@ import { useCameraControls } from '@/hooks/useCameraControls'
import { zoomToSelection } from '../ui-overrides'
//const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev`
-export const WORKER_URL = 'https://jeffemmett-canvas.jeffemmett.workers.dev';
+export const WORKER_URL = import.meta.env.VITE_TLDRAW_WORKER_URL || 'https://jeffemmett-canvas.jeffemmett.workers.dev';
const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape]
const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools
diff --git a/worker/worker.ts b/worker/worker.ts
index b99b8ca..4acdcf2 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -21,7 +21,11 @@ const securityHeaders = {
// we're hosting the worker separately to the client. you should restrict this to your own domain.
const { preflight, corsify } = cors({
origin: (origin) => {
- if (!origin) return undefined
+ const allowedOrigins = [
+ 'https://jeffemmett.com',
+ 'https://www.jeffemmett.com',
+ 'https://jeffemmett-canvas.jeffemmett.workers.dev'
+ ];
const allowedPatterns = [
// Localhost with any port
@@ -41,14 +45,20 @@ const { preflight, corsify } = cors({
/^https:\/\/jeffemmett-canvas\.jeffemmett\.workers\.dev$/
]
- // Check if origin matches any of our patterns
- const isAllowed = allowedPatterns.some(pattern =>
- pattern instanceof RegExp
- ? pattern.test(origin)
- : pattern === origin
- )
- return isAllowed ? origin : undefined
+ if (!origin) return undefined;
+
+ // Check exact matches first
+ if (allowedOrigins.includes(origin)) {
+ return origin;
+ }
+
+ // Then check patterns
+ if (allowedPatterns.some(pattern => pattern.test(origin))) {
+ return origin;
+ }
+
+ return undefined;
},
allowMethods: ['GET', 'POST', 'OPTIONS', 'UPGRADE'],
allowHeaders: [
@@ -59,8 +69,7 @@ const { preflight, corsify } = cors({
'Sec-WebSocket-Key',
'Sec-WebSocket-Version',
'Sec-WebSocket-Extensions',
- 'Sec-WebSocket-Protocol',
- ...Object.keys(securityHeaders)
+ 'Sec-WebSocket-Protocol'
],
maxAge: 86400,
})
From 6f6c924f6606e2f304a0e43af1f5f5d48f080076 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 13:27:37 -0500
Subject: [PATCH 05/16] fix VITE_ worker URL
---
src/components/Board.tsx | 7 ++--
vite.config.ts | 72 +++++++++++++++++++++++-----------------
2 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index 9d2f9ee..c4d22c7 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -26,8 +26,11 @@ import { components, uiOverrides } from '@/ui-overrides'
import { useCameraControls } from '@/hooks/useCameraControls'
import { zoomToSelection } from '../ui-overrides'
-//const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev`
-export const WORKER_URL = import.meta.env.VITE_TLDRAW_WORKER_URL || 'https://jeffemmett-canvas.jeffemmett.workers.dev';
+// Default to production URL if env var isn't available
+const DEFAULT_WORKER_URL = 'https://jeffemmett-canvas.jeffemmett.workers.dev';
+export const WORKER_URL = typeof import.meta.env.VITE_TLDRAW_WORKER_URL === 'string'
+ ? import.meta.env.VITE_TLDRAW_WORKER_URL
+ : DEFAULT_WORKER_URL;
const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape]
const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools
diff --git a/vite.config.ts b/vite.config.ts
index 3927bd7..7aba30b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,38 +1,48 @@
import { markdownPlugin } from './build/markdownPlugin';
-import { defineConfig } from 'vite'
+import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
import { viteStaticCopy } from 'vite-plugin-static-copy';
-export default defineConfig({
- envPrefix: ['VITE_'],
- plugins: [
- react(),
- wasm(),
- topLevelAwait(),
- markdownPlugin,
- viteStaticCopy({
- targets: [
- {
- src: 'src/posts/',
- dest: '.'
- }
- ]
- })
- ],
- 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.
+ const env = loadEnv(mode, process.cwd(), '');
+
+ return {
+ envPrefix: ['VITE_'],
+ plugins: [
+ react(),
+ wasm(),
+ topLevelAwait(),
+ markdownPlugin,
+ viteStaticCopy({
+ targets: [
+ {
+ src: 'src/posts/',
+ dest: '.'
+ }
+ ]
+ })
+ ],
+ server: {
+ host: '0.0.0.0',
+ port: 5173,
},
- },
-})
+ build: {
+ sourcemap: true,
+ },
+ base: '/',
+ publicDir: 'src/public',
+ resolve: {
+ alias: {
+ '@': '/src',
+ },
+ },
+ define: {
+ 'import.meta.env.VITE_TLDRAW_WORKER_URL': JSON.stringify(
+ env.VITE_TLDRAW_WORKER_URL || 'https://jeffemmett-canvas.jeffemmett.workers.dev'
+ )
+ }
+ };
+});
From c4198e1faff4b98c83fa52aa42282f0af128d63d Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 13:31:37 -0500
Subject: [PATCH 06/16] add vite env types
---
src/vite-env.d.ts | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 11f02fe..f90643f 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1 +1,9 @@
///
+
+interface ImportMetaEnv {
+ readonly VITE_TLDRAW_WORKER_URL: string
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
From 97b00c1569cf765c20e6b322efeb3f6ec5e7c246 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 13:43:56 -0500
Subject: [PATCH 07/16] fix prod env
---
src/components/Board.tsx | 5 +--
vite.config.ts | 68 +++++++++++++++++-----------------------
2 files changed, 30 insertions(+), 43 deletions(-)
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index c4d22c7..669f970 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -27,10 +27,7 @@ import { useCameraControls } from '@/hooks/useCameraControls'
import { zoomToSelection } from '../ui-overrides'
// Default to production URL if env var isn't available
-const DEFAULT_WORKER_URL = 'https://jeffemmett-canvas.jeffemmett.workers.dev';
-export const WORKER_URL = typeof import.meta.env.VITE_TLDRAW_WORKER_URL === 'string'
- ? import.meta.env.VITE_TLDRAW_WORKER_URL
- : DEFAULT_WORKER_URL;
+export const WORKER_URL = 'https://jeffemmett-canvas.jeffemmett.workers.dev';
const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape]
const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools
diff --git a/vite.config.ts b/vite.config.ts
index 7aba30b..81c5b98 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -5,44 +5,34 @@ import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";
import { viteStaticCopy } from 'vite-plugin-static-copy';
-export default defineConfig(({ mode }) => {
- // Load env file based on `mode` in the current working directory.
- const env = loadEnv(mode, process.cwd(), '');
-
- return {
- envPrefix: ['VITE_'],
- plugins: [
- react(),
- wasm(),
- topLevelAwait(),
- markdownPlugin,
- viteStaticCopy({
- targets: [
- {
- src: 'src/posts/',
- dest: '.'
- }
- ]
- })
- ],
- server: {
- host: '0.0.0.0',
- port: 5173,
+export default defineConfig({
+ envPrefix: ['VITE_'],
+ plugins: [
+ react(),
+ wasm(),
+ topLevelAwait(),
+ markdownPlugin,
+ viteStaticCopy({
+ targets: [
+ {
+ src: 'src/posts/',
+ dest: '.'
+ }
+ ]
+ })
+ ],
+ server: {
+ host: '0.0.0.0',
+ port: 5173,
+ },
+ build: {
+ sourcemap: true,
+ },
+ base: '/',
+ publicDir: 'src/public',
+ resolve: {
+ alias: {
+ '@': '/src',
},
- build: {
- sourcemap: true,
- },
- base: '/',
- publicDir: 'src/public',
- resolve: {
- alias: {
- '@': '/src',
- },
- },
- define: {
- 'import.meta.env.VITE_TLDRAW_WORKER_URL': JSON.stringify(
- env.VITE_TLDRAW_WORKER_URL || 'https://jeffemmett-canvas.jeffemmett.workers.dev'
- )
- }
- };
+ }
});
From 71fc07133a2270f4a71610ebb51abe1c20b3dd40 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 14:33:31 -0500
Subject: [PATCH 08/16] fix CORS for prod env
---
worker/worker.ts | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/worker/worker.ts b/worker/worker.ts
index 4acdcf2..473d4ae 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -27,24 +27,14 @@ const { preflight, corsify } = cors({
'https://jeffemmett-canvas.jeffemmett.workers.dev'
];
- const allowedPatterns = [
- // Localhost with any port
+ // Development patterns
+ const devPatterns = [
/^http:\/\/localhost:\d+$/,
- // 127.0.0.1 with any port
/^http:\/\/127\.0\.0\.1:\d+$/,
- // 192.168.*.* with any port
/^http:\/\/192\.168\.\d+\.\d+:\d+$/,
- // 169.254.*.* with any port
/^http:\/\/169\.254\.\d+\.\d+:\d+$/,
- // 10.*.*.* with any port
- /^http:\/\/10\.\d+\.\d+\.\d+:\d+$/,
- // Production domain
- /^https:\/\/jeffemmett\.com$/,
- /^https:\/\/www\.jeffemmett\.com$/,
- // Worker domain
- /^https:\/\/jeffemmett-canvas\.jeffemmett\.workers\.dev$/
- ]
-
+ /^http:\/\/10\.\d+\.\d+\.\d+:\d+$/
+ ];
if (!origin) return undefined;
@@ -53,8 +43,8 @@ const { preflight, corsify } = cors({
return origin;
}
- // Then check patterns
- if (allowedPatterns.some(pattern => pattern.test(origin))) {
+ // Then check development patterns
+ if (process.env.NODE_ENV === 'development' && devPatterns.some(pattern => pattern.test(origin))) {
return origin;
}
@@ -72,6 +62,7 @@ const { preflight, corsify } = cors({
'Sec-WebSocket-Protocol'
],
maxAge: 86400,
+ credentials: true
})
const router = AutoRouter({
before: [preflight],
From 632e7979a226927016db412ad4908609bfb29a14 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 14:39:57 -0500
Subject: [PATCH 09/16] fix CORS
---
worker/TldrawDurableObject.ts | 16 +++++++++++++++-
worker/worker.ts | 36 +++++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index a101989..e958bb6 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -97,7 +97,21 @@ export class TldrawDurableObject {
// `fetch` is the entry point for all requests to the Durable Object
fetch(request: Request): Response | Promise {
- return this.router.fetch(request)
+ try {
+ return this.router.fetch(request)
+ } catch (err) {
+ console.error('Error in DO fetch:', err);
+ return new Response(JSON.stringify({
+ error: 'Internal Server Error',
+ message: (err as Error).message
+ }), {
+ status: 500,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*'
+ }
+ });
+ }
}
// what happens when someone tries to connect to this room?
diff --git a/worker/worker.ts b/worker/worker.ts
index 473d4ae..c81ad19 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -133,3 +133,39 @@ const router = AutoRouter({
// export our router for cloudflare
export default router
+
+const corsHeaders = {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ 'Access-Control-Max-Age': '86400',
+};
+
+// Add CORS headers to all responses
+function handleCors(request: Request) {
+ // Handle preflight requests
+ if (request.method === 'OPTIONS') {
+ return new Response(null, {
+ headers: corsHeaders
+ });
+ }
+
+ return null;
+}
+
+// Modify the fetch handler
+async function handleRequest(request: Request) {
+ // Handle CORS preflight
+ const corsResult = handleCors(request);
+ if (corsResult) return corsResult;
+
+ // Handle the actual request
+ const response = await router.handle(request);
+
+ // Add CORS headers to the response
+ Object.entries(corsHeaders).forEach(([key, value]) => {
+ response.headers.set(key, value);
+ });
+
+ return response;
+}
From 3006e85375fb2a60c2be1796476eeebfb6b75efa Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 14:57:05 -0500
Subject: [PATCH 10/16] fix prod env
---
worker/TldrawDurableObject.ts | 6 +++++-
worker/worker.ts | 18 +++++-------------
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index e958bb6..a362bca 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -108,7 +108,11 @@ export class TldrawDurableObject {
status: 500,
headers: {
'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*'
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, UPGRADE',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Upgrade, Connection',
+ 'Access-Control-Max-Age': '86400',
+ 'Access-Control-Allow-Credentials': 'true'
}
});
}
diff --git a/worker/worker.ts b/worker/worker.ts
index c81ad19..c319f2d 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -27,24 +27,16 @@ const { preflight, corsify } = cors({
'https://jeffemmett-canvas.jeffemmett.workers.dev'
];
- // Development patterns
- const devPatterns = [
- /^http:\/\/localhost:\d+$/,
- /^http:\/\/127\.0\.0\.1:\d+$/,
- /^http:\/\/192\.168\.\d+\.\d+:\d+$/,
- /^http:\/\/169\.254\.\d+\.\d+:\d+$/,
- /^http:\/\/10\.\d+\.\d+\.\d+:\d+$/
- ];
+ // Always allow if no origin (like from a local file)
+ if (!origin) return '*';
- if (!origin) return undefined;
-
- // Check exact matches first
+ // Check exact matches
if (allowedOrigins.includes(origin)) {
return origin;
}
- // Then check development patterns
- if (process.env.NODE_ENV === 'development' && devPatterns.some(pattern => pattern.test(origin))) {
+ // For development - check if it's a localhost or local IP
+ if (origin.match(/^http:\/\/(localhost|127\.0\.0\.192\.168\.|169\.254\.|10\.)/)) {
return origin;
}
From 08175d3a7c18bb10bc3baa523a7b255b9564f4c2 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:03:53 -0500
Subject: [PATCH 11/16] fix CORS
---
worker/TldrawDurableObject.ts | 11 ++++++++++-
worker/worker.ts | 36 -----------------------------------
2 files changed, 10 insertions(+), 37 deletions(-)
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index a362bca..79640ef 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -136,7 +136,16 @@ export class TldrawDurableObject {
room.handleSocketConnect({ sessionId, socket: serverWebSocket })
// return the websocket connection to the client
- return new Response(null, { status: 101, webSocket: clientWebSocket })
+ return new Response(null, {
+ status: 101,
+ webSocket: clientWebSocket,
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, UPGRADE',
+ 'Access-Control-Allow-Headers': '*',
+ 'Access-Control-Allow-Credentials': 'true'
+ }
+ });
}
getRoom() {
diff --git a/worker/worker.ts b/worker/worker.ts
index c319f2d..00456a0 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -125,39 +125,3 @@ const router = AutoRouter({
// export our router for cloudflare
export default router
-
-const corsHeaders = {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
- 'Access-Control-Max-Age': '86400',
-};
-
-// Add CORS headers to all responses
-function handleCors(request: Request) {
- // Handle preflight requests
- if (request.method === 'OPTIONS') {
- return new Response(null, {
- headers: corsHeaders
- });
- }
-
- return null;
-}
-
-// Modify the fetch handler
-async function handleRequest(request: Request) {
- // Handle CORS preflight
- const corsResult = handleCors(request);
- if (corsResult) return corsResult;
-
- // Handle the actual request
- const response = await router.handle(request);
-
- // Add CORS headers to the response
- Object.entries(corsHeaders).forEach(([key, value]) => {
- response.headers.set(key, value);
- });
-
- return response;
-}
From 39e6cccc3f92bf43e43bdcec5fe94317860fdd44 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:10:25 -0500
Subject: [PATCH 12/16] fix CORS
---
worker/TldrawDurableObject.ts | 17 +++++++----------
worker/worker.ts | 6 +++++-
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index 79640ef..7bf5ce0 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -120,32 +120,29 @@ export class TldrawDurableObject {
// what happens when someone tries to connect to this room?
async handleConnect(request: IRequest): Promise {
- // extract query params from request
const sessionId = request.query.sessionId as string
if (!sessionId) return error(400, 'Missing sessionId')
- // Create the websocket pair for the client
const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair()
- // @ts-ignore
serverWebSocket.accept()
- // load the room, or retrieve it if it's already loaded
const room = await this.getRoom()
-
- // connect the client to the room
room.handleSocketConnect({ sessionId, socket: serverWebSocket })
- // return the websocket connection to the client
+ const origin = request.headers.get('Origin') || '*'
+
return new Response(null, {
status: 101,
webSocket: clientWebSocket,
headers: {
- 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, UPGRADE',
'Access-Control-Allow-Headers': '*',
- 'Access-Control-Allow-Credentials': 'true'
+ 'Access-Control-Allow-Credentials': 'true',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade'
}
- });
+ })
}
getRoom() {
diff --git a/worker/worker.ts b/worker/worker.ts
index 00456a0..b51aee2 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -76,7 +76,11 @@ const router = AutoRouter({
.get('/connect/:roomId', (request, env) => {
const id = env.TLDRAW_DURABLE_OBJECT.idFromName(request.params.roomId)
const room = env.TLDRAW_DURABLE_OBJECT.get(id)
- return room.fetch(request.url, { headers: request.headers, body: request.body })
+ return room.fetch(request.url, {
+ headers: request.headers,
+ body: request.body,
+ method: request.method
+ })
})
// assets can be uploaded to the bucket under /uploads:
From 111be039074aed93b5062038f4ba2427c728866e Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:23:56 -0500
Subject: [PATCH 13/16] swap persistentboard with Tldraw native sync
---
src/components/Board copy.tsx | 123 -------------------------------
src/components/Board.tsx | 16 ++--
src/hooks/useGSetState.ts | 40 ----------
src/hooks/useLocalStorageRoom.ts | 20 -----
src/hooks/usePersistentBoard.ts | 92 -----------------------
worker/TldrawDurableObject.ts | 19 +++--
6 files changed, 24 insertions(+), 286 deletions(-)
delete mode 100644 src/components/Board copy.tsx
delete mode 100644 src/hooks/useGSetState.ts
delete mode 100644 src/hooks/useLocalStorageRoom.ts
delete mode 100644 src/hooks/usePersistentBoard.ts
diff --git a/src/components/Board copy.tsx b/src/components/Board copy.tsx
deleted file mode 100644
index b0c851d..0000000
--- a/src/components/Board copy.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import { useSync } from '@tldraw/sync'
-import {
- AssetRecordType,
- getHashForString,
- TLBookmarkAsset,
- Tldraw,
- // useLocalStorageState,
-} from 'tldraw'
-import { useParams } from 'react-router-dom'
-import useLocalStorageState from 'use-local-storage-state'
-import { ChatBoxTool } from '@/tools/ChatBoxTool'
-import { ChatBoxShape } from '@/shapes/ChatBoxShapeUtil'
-import { VideoChatTool } from '@/tools/VideoChatTool'
-import { VideoChatShape } from '@/shapes/VideoChatShapeUtil'
-import { multiplayerAssetStore } from '../client/multiplayerAssetStore'
-import { customSchema } from '../../worker/TldrawDurableObject'
-import { EmbedShape } from '@/shapes/EmbedShapeUtil'
-import { EmbedTool } from '@/tools/EmbedTool'
-
-import React, { useEffect, useState } from 'react';
-import { ChatBox } from '@/shapes/ChatBoxShapeUtil';
-import { components, uiOverrides } from '@/ui-overrides'
-
-const WORKER_URL = `https://jeffemmett-canvas.jeffemmett.workers.dev`
-
-const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape]
-const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools
-
-export function Board() {
- const { slug } = useParams<{ slug: string }>(); // Ensure this is inside the Board component
- const roomId = slug || 'default-room'; // Declare roomId here
-
- const store = useSync({
- uri: `${WORKER_URL}/connect/${roomId}`,
- assets: multiplayerAssetStore,
- shapeUtils: shapeUtils,
- schema: customSchema,
- });
-
- const [isChatBoxVisible, setChatBoxVisible] = useState(false);
- const [userName, setUserName] = useState('');
- const [isVideoChatVisible, setVideoChatVisible] = useState(false); // Added state for video chat visibility
-
- const handleNameChange = (event: React.ChangeEvent) => {
- setUserName(event.target.value);
- };
-
- const [persistedStore, setPersistedStore] = useLocalStorageState('board-store', { defaultValue: store }
- )
-
- useEffect(() => {
- setPersistedStore(store);
- }, [store]);
-
- return (
-
- )
-}
-
-// How does our server handle bookmark unfurling?
-async function unfurlBookmarkUrl({ url }: { url: string }): Promise {
- const asset: TLBookmarkAsset = {
- id: AssetRecordType.createId(getHashForString(url)),
- typeName: 'asset',
- type: 'bookmark',
- meta: {},
- props: {
- src: url,
- description: '',
- image: '',
- favicon: '',
- title: '',
- },
- }
-
- try {
- const response = await fetch(`${WORKER_URL}/unfurl?url=${encodeURIComponent(url)}`)
- const data = await response.json() as { description: string, image: string, favicon: string, title: string }
-
- asset.props.description = data?.description ?? ''
- asset.props.image = data?.image ?? ''
- asset.props.favicon = data?.favicon ?? ''
- asset.props.title = data?.title ?? ''
- } catch (e) {
- console.error(e)
- }
-
- return asset
-}
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index 669f970..dc2ee77 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -19,6 +19,7 @@ import { multiplayerAssetStore } from '../client/multiplayerAssetStore'
import { customSchema } from '../../worker/TldrawDurableObject'
import { EmbedShape } from '@/shapes/EmbedShapeUtil'
import { EmbedTool } from '@/tools/EmbedTool'
+import { defaultShapeUtils, defaultBindingUtils } from 'tldraw'
import React, { useState, useEffect, useCallback } from 'react';
import { ChatBox } from '@/shapes/ChatBoxShapeUtil';
@@ -32,16 +33,19 @@ export const WORKER_URL = 'https://jeffemmett-canvas.jeffemmett.workers.dev';
const shapeUtils = [ChatBoxShape, VideoChatShape, EmbedShape]
const tools = [ChatBoxTool, VideoChatTool, EmbedTool]; // Array of tools
-// Add these imports
-import { useGSetState } from '@/hooks/useGSetState';
-import { useLocalStorageRoom } from '@/hooks/useLocalStorageRoom';
-import { usePersistentBoard } from '@/hooks/usePersistentBoard';
-
export function Board() {
const { slug } = useParams<{ slug: string }>();
const roomId = slug || 'default-room';
- const store = usePersistentBoard(roomId);
+
+ const store = useSync({
+ uri: `${WORKER_URL}/connect/${roomId}`,
+ assets: multiplayerAssetStore,
+ shapeUtils: [...shapeUtils, ...defaultShapeUtils],
+ // Add default bindings if you're using them
+ bindingUtils: [...defaultBindingUtils],
+ })
+
const [editor, setEditor] = useState(null)
const { zoomToFrame, copyFrameLink, copyLocationLink, revertCamera } = useCameraControls(editor)
diff --git a/src/hooks/useGSetState.ts b/src/hooks/useGSetState.ts
deleted file mode 100644
index e825fa1..0000000
--- a/src/hooks/useGSetState.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import useLocalStorageState from 'use-local-storage-state';
-import GSet from 'crdts/src/G-Set';
-import { TLRecord } from 'tldraw';
-import { useRef, useCallback } from 'react';
-
-export function useGSetState(roomId: string) {
- const [localSet, setLocalSet] = useLocalStorageState(`gset-${roomId}`, {
- defaultValue: []
- });
-
- // Keep GSet instance in a ref to persist between renders
- const gsetRef = useRef>();
- if (!gsetRef.current) {
- gsetRef.current = new GSet();
- // Initialize G-Set with local data
- if (localSet && Array.isArray(localSet)) {
- localSet.forEach(record => gsetRef.current?.add(record));
- }
- }
-
- const addRecord = useCallback((record: TLRecord) => {
- if (!gsetRef.current) return;
- gsetRef.current.add(record);
- setLocalSet(Array.from(gsetRef.current.values()));
- }, [setLocalSet]);
-
- const merge = useCallback((remoteSet: Set) => {
- if (!gsetRef.current) return new Set();
- remoteSet.forEach(record => gsetRef.current?.add(record));
- setLocalSet(Array.from(gsetRef.current.values()));
- return gsetRef.current.values();
- }, [setLocalSet]);
-
- return {
- values: gsetRef.current.values(),
- add: addRecord,
- merge,
- localSet
- };
-}
\ No newline at end of file
diff --git a/src/hooks/useLocalStorageRoom.ts b/src/hooks/useLocalStorageRoom.ts
deleted file mode 100644
index 946fef5..0000000
--- a/src/hooks/useLocalStorageRoom.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import useLocalStorageState from 'use-local-storage-state';
-import { TLRecord, createTLStore, SerializedStore } from 'tldraw';
-import { customSchema } from '../../worker/TldrawDurableObject';
-
-export function useLocalStorageRoom(roomId: string) {
- const [records, setRecords] = useLocalStorageState>(`tldraw-room-${roomId}`, {
- defaultValue: createTLStore({ schema: customSchema }).serialize()
- });
-
- const store = createTLStore({
- schema: customSchema,
- initialData: records,
- });
-
- return {
- store,
- records,
- setRecords
- };
-}
\ No newline at end of file
diff --git a/src/hooks/usePersistentBoard.ts b/src/hooks/usePersistentBoard.ts
deleted file mode 100644
index 128bf51..0000000
--- a/src/hooks/usePersistentBoard.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { useSync } from '@tldraw/sync'
-import { useState, useEffect, useCallback, useRef } from 'react'
-import { customSchema } from '../../worker/TldrawDurableObject'
-import { multiplayerAssetStore } from '../client/multiplayerAssetStore'
-import { useGSetState } from './useGSetState'
-import { useLocalStorageRoom } from './useLocalStorageRoom'
-import { TLRecord } from 'tldraw'
-import { WORKER_URL } from '../components/Board'
-
-export function usePersistentBoard(roomId: string) {
- const [isOnline, setIsOnline] = useState(navigator.onLine)
- const { store: localStore, records, setRecords } = useLocalStorageRoom(roomId)
- const { values, add, merge } = useGSetState(roomId)
- const initialSyncRef = useRef(false)
- const mergeInProgressRef = useRef(false)
-
- const syncedStore = useSync({
- uri: `${WORKER_URL.replace('https://', 'wss://')}/connect/${roomId}`,
- schema: customSchema,
- assets: multiplayerAssetStore,
- })
-
- useEffect(() => {
- const handleOnline = () => setIsOnline(true)
- const handleOffline = () => setIsOnline(false)
-
- window.addEventListener('online', handleOnline)
- window.addEventListener('offline', handleOffline)
-
- return () => {
- window.removeEventListener('online', handleOnline)
- window.removeEventListener('offline', handleOffline)
- }
- }, [])
-
- const mergeRecords = useCallback((records: Set) => {
- if (mergeInProgressRef.current || records.size === 0) return
-
- try {
- mergeInProgressRef.current = true
- merge(records)
- if (!isOnline && localStore) {
- setRecords(localStore.serialize())
- }
- } finally {
- mergeInProgressRef.current = false
- }
- }, [isOnline, localStore, merge, setRecords])
-
- useEffect(() => {
- if (!syncedStore?.store || !localStore) return
-
- if (isOnline && !initialSyncRef.current) {
- initialSyncRef.current = true
- const serverRecords = Object.values(syncedStore.store.allRecords())
- if (serverRecords.length > 0) {
- mergeRecords(new Set(serverRecords))
- }
-
- const unsubscribe = syncedStore.store.listen((event) => {
- if ('changes' in event) {
- const changedRecords = Object.values(event.changes)
- if (changedRecords.length > 0) {
- mergeRecords(new Set(changedRecords))
- }
- }
- })
-
- return () => unsubscribe()
- } else if (!isOnline) {
- const currentRecords = Object.values(localStore.allRecords())
- if (currentRecords.length > 0) {
- mergeRecords(new Set(currentRecords))
- }
- }
- }, [isOnline, syncedStore?.store, localStore, mergeRecords])
-
- const addRecord = useCallback((record: TLRecord) => {
- if (!record) return
- add(record)
- if (!isOnline && localStore) {
- setRecords(localStore.serialize())
- }
- }, [add, isOnline, localStore, setRecords])
-
- return {
- store: isOnline ? syncedStore?.store : localStore,
- isOnline,
- addRecord,
- mergeRecords
- }
-}
\ No newline at end of file
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index 7bf5ce0..2f97034 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -5,7 +5,7 @@ import {
TLRecord,
TLShape,
createTLSchema,
- // defaultBindingSchemas,
+ defaultBindingSchemas,
defaultShapeSchemas,
} from '@tldraw/tlschema'
import { AutoRouter, IRequest, error } from 'itty-router'
@@ -20,11 +20,20 @@ import GSet from 'crdts/src/G-Set'
export const customSchema = createTLSchema({
shapes: {
...defaultShapeSchemas,
- ChatBox: ChatBoxShape,
- VideoChat: VideoChatShape,
- Embed: EmbedShape
+ ChatBox: {
+ props: ChatBoxShape.props,
+ migrations: ChatBoxShape.migrations,
+ },
+ VideoChat: {
+ props: VideoChatShape.props,
+ migrations: VideoChatShape.migrations,
+ },
+ Embed: {
+ props: EmbedShape.props,
+ migrations: EmbedShape.migrations,
+ },
},
- // bindings: { ...defaultBindingSchemas },
+ bindings: defaultBindingSchemas,
})
// each whiteboard room is hosted in a DurableObject:
From 110fc19b94d896368f5458dc0ba2cb5ef739b1d7 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:35:53 -0500
Subject: [PATCH 14/16] one more attempt
---
src/components/Board.tsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/Board.tsx b/src/components/Board.tsx
index dc2ee77..3dea2ef 100644
--- a/src/components/Board.tsx
+++ b/src/components/Board.tsx
@@ -1,4 +1,5 @@
import { useSync } from '@tldraw/sync'
+import { useMemo } from 'react'
import {
AssetRecordType,
getHashForString,
@@ -10,7 +11,6 @@ import {
TLUiEventSource,
} from 'tldraw'
import { useParams } from 'react-router-dom'
-import useLocalStorageState from 'use-local-storage-state'
import { ChatBoxTool } from '@/tools/ChatBoxTool'
import { ChatBoxShape } from '@/shapes/ChatBoxShapeUtil'
import { VideoChatTool } from '@/tools/VideoChatTool'
@@ -38,14 +38,14 @@ export function Board() {
const { slug } = useParams<{ slug: string }>();
const roomId = slug || 'default-room';
- const store = useSync({
+ const storeConfig = useMemo(() => ({
uri: `${WORKER_URL}/connect/${roomId}`,
assets: multiplayerAssetStore,
shapeUtils: [...shapeUtils, ...defaultShapeUtils],
- // Add default bindings if you're using them
bindingUtils: [...defaultBindingUtils],
- })
+ }), [roomId]);
+ const store = useSync(storeConfig);
const [editor, setEditor] = useState(null)
const { zoomToFrame, copyFrameLink, copyLocationLink, revertCamera } = useCameraControls(editor)
From 2e0a05ab32fcbacd23d654181b6a3c5e08ee93ac Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:50:37 -0500
Subject: [PATCH 15/16] fix vite config
---
vite.config.ts | 3 ++
worker/TldrawDurableObject.ts | 61 ++++++++++++++++++++++++-----------
2 files changed, 45 insertions(+), 19 deletions(-)
diff --git a/vite.config.ts b/vite.config.ts
index 81c5b98..48b47b5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -34,5 +34,8 @@ export default defineConfig({
alias: {
'@': '/src',
},
+ },
+ define: {
+ 'import.meta.env.VITE_WORKER_URL': JSON.stringify(process.env.VITE_WORKER_URL)
}
});
diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts
index 2f97034..66c725a 100644
--- a/worker/TldrawDurableObject.ts
+++ b/worker/TldrawDurableObject.ts
@@ -129,29 +129,52 @@ export class TldrawDurableObject {
// what happens when someone tries to connect to this room?
async handleConnect(request: IRequest): Promise {
- const sessionId = request.query.sessionId as string
- if (!sessionId) return error(400, 'Missing sessionId')
+ if (!this.roomId) {
+ return new Response('Room not initialized', { status: 400 });
+ }
- const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair()
- serverWebSocket.accept()
+ const sessionId = request.query.sessionId as string;
+ if (!sessionId) {
+ return new Response('Missing sessionId', { status: 400 });
+ }
- const room = await this.getRoom()
- room.handleSocketConnect({ sessionId, socket: serverWebSocket })
+ const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair();
- const origin = request.headers.get('Origin') || '*'
+ try {
+ serverWebSocket.accept();
+ const room = await this.getRoom();
- return new Response(null, {
- status: 101,
- webSocket: clientWebSocket,
- headers: {
- 'Access-Control-Allow-Origin': origin,
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, UPGRADE',
- 'Access-Control-Allow-Headers': '*',
- 'Access-Control-Allow-Credentials': 'true',
- 'Upgrade': 'websocket',
- 'Connection': 'Upgrade'
- }
- })
+ // Handle socket connection with proper error boundaries
+ room.handleSocketConnect({
+ sessionId,
+ socket: {
+ send: serverWebSocket.send.bind(serverWebSocket),
+ close: serverWebSocket.close.bind(serverWebSocket),
+ addEventListener: serverWebSocket.addEventListener.bind(serverWebSocket),
+ removeEventListener: serverWebSocket.removeEventListener.bind(serverWebSocket),
+ readyState: serverWebSocket.readyState,
+ }
+ });
+
+ return new Response(null, {
+ status: 101,
+ webSocket: clientWebSocket,
+ headers: {
+ 'Access-Control-Allow-Origin': request.headers.get('Origin') || '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, UPGRADE',
+ 'Access-Control-Allow-Headers': '*',
+ 'Access-Control-Allow-Credentials': 'true',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade'
+ }
+ });
+ } catch (error) {
+ console.error('WebSocket connection error:', error);
+ serverWebSocket.close(1011, 'Failed to initialize connection');
+ return new Response('Failed to establish WebSocket connection', {
+ status: 500
+ });
+ }
}
getRoom() {
From e286a120f172bc0071cc835c954bdf61747118a3 Mon Sep 17 00:00:00 2001
From: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
Date: Sat, 7 Dec 2024 16:02:10 -0500
Subject: [PATCH 16/16] maybe this works
---
worker/worker.ts | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/worker/worker.ts b/worker/worker.ts
index b51aee2..6c0d900 100644
--- a/worker/worker.ts
+++ b/worker/worker.ts
@@ -24,7 +24,8 @@ const { preflight, corsify } = cors({
const allowedOrigins = [
'https://jeffemmett.com',
'https://www.jeffemmett.com',
- 'https://jeffemmett-canvas.jeffemmett.workers.dev'
+ 'https://jeffemmett-canvas.jeffemmett.workers.dev',
+ 'https://jeffemmett.com/board/*',
];
// Always allow if no origin (like from a local file)
@@ -92,11 +93,14 @@ const router = AutoRouter({
// bookmarks need to extract metadata from pasted URLs:
.get('/unfurl', handleUnfurlRequest)
- .get('/room/:roomId', async (request, env) => {
+ .get('/room/:roomId', (request, env) => {
const id = env.TLDRAW_DURABLE_OBJECT.idFromName(request.params.roomId)
const room = env.TLDRAW_DURABLE_OBJECT.get(id)
- const response = await room.fetch(request.url)
- return response
+ return room.fetch(request.url, {
+ headers: request.headers,
+ body: request.body,
+ method: request.method
+ })
})
.post('/room/:roomId', async (request, env) => {