diff --git a/apps/dotcom-worker/src/lib/routes/createRoom.ts b/apps/dotcom-worker/src/lib/routes/createRoom.ts index 43899c56e..4189d2154 100644 --- a/apps/dotcom-worker/src/lib/routes/createRoom.ts +++ b/apps/dotcom-worker/src/lib/routes/createRoom.ts @@ -5,15 +5,11 @@ import { nanoid } from 'nanoid' import { getR2KeyForRoom } from '../r2' import { Environment } from '../types' import { validateSnapshot } from '../utils/validateSnapshot' -import { isAllowedOrigin } from '../worker' // Sets up a new room based on a provided snapshot, e.g. when a user clicks the "Share" buttons or the "Fork project" buttons. export async function createRoom(request: IRequest, env: Environment): Promise { // The data sent from the client will include the data for the new room const data = (await request.json()) as CreateRoomRequestBody - if (!isAllowedOrigin(data.origin)) { - return Response.json({ error: true, message: 'Not allowed' }, { status: 406 }) - } // There's a chance the data will be invalid, so we check it first const snapshotResult = validateSnapshot(data.snapshot) diff --git a/apps/dotcom-worker/src/lib/worker.ts b/apps/dotcom-worker/src/lib/worker.ts index 88bbc4e9e..f8fdf7ca0 100644 --- a/apps/dotcom-worker/src/lib/worker.ts +++ b/apps/dotcom-worker/src/lib/worker.ts @@ -87,6 +87,7 @@ const Worker = { } export function isAllowedOrigin(origin: string) { + if (env.IS_LOCAL === 'true') return true if (origin === 'http://localhost:3000') return true if (origin === 'http://localhost:5420') return true if (origin.endsWith('.tldraw.com')) return true @@ -94,7 +95,7 @@ export function isAllowedOrigin(origin: string) { return false } -async function blockUnknownOrigins(request: Request) { +async function blockUnknownOrigins(request: Request, env: Environment) { // allow requests for the same origin (new rewrite routing for SPA) if (request.headers.get('sec-fetch-site') === 'same-origin') { return undefined diff --git a/apps/dotcom-worker/wrangler.toml b/apps/dotcom-worker/wrangler.toml index 6b72e9d1b..b5d904b61 100644 --- a/apps/dotcom-worker/wrangler.toml +++ b/apps/dotcom-worker/wrangler.toml @@ -3,6 +3,7 @@ compatibility_date = "2023-10-16" [dev] port = 8787 +ip = "0.0.0.0" # these migrations are append-only. you can't change them. if you do need to change something, do so # by creating new migrations diff --git a/apps/dotcom/scripts/build.ts b/apps/dotcom/scripts/build.ts index 69373d5f8..f25dd0f3d 100644 --- a/apps/dotcom/scripts/build.ts +++ b/apps/dotcom/scripts/build.ts @@ -48,6 +48,8 @@ async function build() { await exec('cp', ['-r', 'dist', '.vercel/output/static']) await exec('rm', ['-rf', ...glob.sync('.vercel/output/static/**/*.js.map')]) + const multiplayerServerUrl = getMultiplayerServerURL() ?? 'http://localhost:8787' + writeFileSync( '.vercel/output/config.json', JSON.stringify( @@ -57,7 +59,7 @@ async function build() { // rewrite api calls to the multiplayer server { src: '^/api(/(.*))?$', - dest: `${getMultiplayerServerURL()}$1`, + dest: `${multiplayerServerUrl}$1`, check: true, }, // cache static assets immutably diff --git a/apps/dotcom/vite.config.ts b/apps/dotcom/vite.config.ts index 8347c19bd..d48fe0f8c 100644 --- a/apps/dotcom/vite.config.ts +++ b/apps/dotcom/vite.config.ts @@ -7,11 +7,26 @@ config({ }) export const getMultiplayerServerURL = () => { - return process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') ?? 'http://127.0.0.1:8787' + return process.env.MULTIPLAYER_SERVER?.replace(/^ws/, 'http') +} + +function urlOrLocalFallback(mode: string, url: string | undefined, localFallbackPort: number) { + if (url) { + return JSON.stringify(url) + } + + if (mode === 'development') { + // in dev, vite lets us inline javascript expressions - so we return a template string that + // will be evaluated on the client + return '`http://${location.hostname}:' + localFallbackPort + '`' + } else { + // in production, we have to fall back to a hardcoded value + return JSON.stringify(`http://localhost:${localFallbackPort}`) + } } // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig((env) => ({ plugins: [react({ tsDecorators: true })], publicDir: './public', build: { @@ -29,8 +44,8 @@ export default defineConfig({ .filter(([key]) => key.startsWith('NEXT_PUBLIC_')) .map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]) ), - 'process.env.MULTIPLAYER_SERVER': JSON.stringify(getMultiplayerServerURL()), - 'process.env.ASSET_UPLOAD': JSON.stringify(process.env.ASSET_UPLOAD ?? 'http://127.0.0.1:8788'), + 'process.env.MULTIPLAYER_SERVER': urlOrLocalFallback(env.mode, getMultiplayerServerURL(), 8787), + 'process.env.ASSET_UPLOAD': urlOrLocalFallback(env.mode, process.env.ASSET_UPLOAD, 8788), 'process.env.TLDRAW_ENV': JSON.stringify(process.env.TLDRAW_ENV ?? 'development'), // Fall back to staging DSN for local develeopment, although you still need to // modify the env check in 'sentry.client.config.ts' to get it reporting errors @@ -42,7 +57,7 @@ export default defineConfig({ server: { proxy: { '/api': { - target: getMultiplayerServerURL(), + target: getMultiplayerServerURL() || 'http://127.0.0.1:8787', rewrite: (path) => path.replace(/^\/api/, ''), ws: false, // we talk to the websocket directly via workers.dev // Useful for debugging proxy issues @@ -64,4 +79,4 @@ export default defineConfig({ }, }, }, -}) +})) diff --git a/packages/editor/src/lib/hooks/useCoarsePointer.ts b/packages/editor/src/lib/hooks/useCoarsePointer.ts index 4e6c5edd5..e9165228d 100644 --- a/packages/editor/src/lib/hooks/useCoarsePointer.ts +++ b/packages/editor/src/lib/hooks/useCoarsePointer.ts @@ -10,29 +10,20 @@ export function useCoarsePointer() { let isCoarse = editor.getInstanceState().isCoarsePointer // 1. - // We'll use touch events / mouse events to detect coarse pointer. + // We'll use pointer events to detect coarse pointer. - // When the user touches the screen, we assume they have a coarse pointer - const handleTouchStart = () => { - if (isCoarse) return - isCoarse = true - editor.updateInstanceState({ isCoarsePointer: true }) + const handlePointerDown = (e: PointerEvent) => { + // when the user interacts with a mouse, we assume they have a fine pointer. + // otherwise, we assume they have a coarse pointer. + const isCoarseEvent = e.pointerType !== 'mouse' + if (isCoarse === isCoarseEvent) return + isCoarse = isCoarseEvent + editor.updateInstanceState({ isCoarsePointer: isCoarseEvent }) } - // When the user moves the mouse, we assume they have a fine pointer - const handleMouseMove = ( - e: MouseEvent & { sourceCapabilities?: { firesTouchEvents: boolean } } - ) => { - if (!isCoarse) return - // Fix Android Chrome bug where mousemove is fired even if the user long presses - if (e.sourceCapabilities?.firesTouchEvents) return - isCoarse = false - editor.updateInstanceState({ isCoarsePointer: false }) - } - - // Set up the listeners for touch and mouse events - window.addEventListener('touchstart', handleTouchStart) - window.addEventListener('mousemove', handleMouseMove) + // we need `capture: true` here because the tldraw component itself stops propagation on + // pointer events it receives. + window.addEventListener('pointerdown', handlePointerDown, { capture: true }) // 2. // We can also use the media query to detect / set the initial pointer type @@ -65,8 +56,7 @@ export function useCoarsePointer() { } return () => { - window.removeEventListener('touchstart', handleTouchStart) - window.removeEventListener('mousemove', handleMouseMove) + window.removeEventListener('pointerdown', handlePointerDown, { capture: true }) if (mql) { mql.removeEventListener('change', handleMediaQueryChange)