Tldraw/apps/dotcom-worker/src/lib/worker.ts

104 wiersze
3.5 KiB
TypeScript

/// <reference no-default-lib="true"/>
/// <reference types="@cloudflare/workers-types" />
import { Router, createCors } from 'itty-router'
import { env } from 'process'
import Toucan from 'toucan-js'
import { createRoom } from './routes/createRoom'
import { createRoomSnapshot } from './routes/createRoomSnapshot'
import { forwardRoomRequest } from './routes/forwardRoomRequest'
import { getRoomHistory } from './routes/getRoomHistory'
import { getRoomHistorySnapshot } from './routes/getRoomHistorySnapshot'
import { getRoomSnapshot } from './routes/getRoomSnapshot'
import { joinExistingRoom } from './routes/joinExistingRoom'
import { Environment } from './types'
import { fourOhFour } from './utils/fourOhFour'
export { TLDrawDurableObject } from './TLDrawDurableObject'
const { preflight, corsify } = createCors({
origins: Object.assign([], { includes: (origin: string) => isAllowedOrigin(origin) }),
})
const router = Router()
.all('*', preflight)
.all('*', blockUnknownOrigins)
.post('/new-room', createRoom)
.post('/snapshots', createRoomSnapshot)
.get('/snapshot/:roomId', getRoomSnapshot)
.get('/r/:roomId', joinExistingRoom)
.get('/r/:roomId/history', getRoomHistory)
.get('/r/:roomId/history/:timestamp', getRoomHistorySnapshot)
.post('/r/:roomId/restore', forwardRoomRequest)
.all('*', fourOhFour)
const Worker = {
fetch(request: Request, env: Environment, context: ExecutionContext) {
const sentry = new Toucan({
dsn: env.SENTRY_DSN,
context, // Includes 'waitUntil', which is essential for Sentry logs to be delivered. Modules workers do not include 'request' in context -- you'll need to set it separately.
request, // request is not included in 'context', so we set it here.
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
})
return router
.handle(request, env, context)
.catch((err) => {
console.error(err)
sentry.captureException(err)
return new Response('Something went wrong', {
status: 500,
statusText: 'Internal Server Error',
})
})
.then((response) => {
const setCookies = response.headers.getAll('set-cookie')
// unfortunately corsify mishandles the set-cookie header, so
// we need to manually add it back in
const result = corsify(response)
if ([...setCookies].length === 0) {
return result
}
const newResponse = new Response(result.body, result)
newResponse.headers.delete('set-cookie')
// add cookies from original response
for (const cookie of setCookies) {
newResponse.headers.append('set-cookie', cookie)
}
return newResponse
})
},
}
function isAllowedOrigin(origin: string) {
if (origin === 'http://localhost:3000') return true
if (origin === 'http://localhost:5420') return true
if (origin.endsWith('.tldraw.com')) return true
if (origin.endsWith('-tldraw.vercel.app')) return true
return false
}
async function blockUnknownOrigins(request: Request) {
// allow requests for the same origin (new rewrite routing for SPA)
if (request.headers.get('sec-fetch-site') === 'same-origin') {
return undefined
}
if (new URL(request.url).pathname === '/auth/callback') {
// allow auth callback because we use the special cookie to verify
// the request
return undefined
}
const origin = request.headers.get('origin')
if (env.IS_LOCAL !== 'true' && (!origin || !isAllowedOrigin(origin))) {
console.error('Attempting to connect from an invalid origin:', origin, env, request)
return new Response('Not allowed', { status: 403 })
}
// origin doesn't match, so we can continue
return undefined
}
export default Worker