| 1 | import { env } from "cloudflare:workers"; |
| 2 | |
| 3 | import { DOCS_VIEWER_HEADER } from "@g1t/contracts"; |
| 4 | |
| 5 | import type { Route } from "./+types/live"; |
| 6 | import { getViewer, roleIn } from "../../../lib/session.server"; |
| 7 | |
| 8 | /** |
| 9 | * A page's live socket: `wss://<site>/<workspace>/-/docs/live?page=<id>`. |
| 10 | * The site checks the session and that the page asking is the site's own, |
| 11 | * then hands the upgrade to the docs service with the viewer, which checks |
| 12 | * their role in the page's space and gives the socket to the page's room |
| 13 | * (one Durable Object per page), which enforces that role. |
| 14 | */ |
| 15 | export async function loader({ params, context, request }: Route.LoaderArgs) { |
| 16 | const viewer = getViewer(context); |
| 17 | if (!viewer) return new Response("Sign in to use Docs.", { status: 401 }); |
| 18 | if (!roleIn(viewer, params.owner)) return new Response("Not found", { status: 404 }); |
| 19 | if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") { |
| 20 | return new Response("This address takes a WebSocket.", { status: 426, headers: { upgrade: "websocket" } }); |
| 21 | } |
| 22 | const origin = request.headers.get("origin"); |
| 23 | if (origin && origin !== new URL(request.url).origin) return new Response("Cross-origin socket refused", { status: 403 }); |
| 24 | const page = new URL(request.url).searchParams.get("page"); |
| 25 | if (!page) return new Response("Which page?", { status: 400 }); |
| 26 | const headers = new Headers(request.headers); |
| 27 | headers.delete("cookie"); |
| 28 | headers.set(DOCS_VIEWER_HEADER, JSON.stringify(viewer)); |
| 29 | const target = `https://docs/live?page=${encodeURIComponent(page)}&workspace=${encodeURIComponent(params.owner.toLowerCase())}`; |
| 30 | try { |
| 31 | return await env.DOCS.fetch(new Request(target, { method: "GET", headers })); |
| 32 | } catch (error) { |
| 33 | console.error("docs: the live socket could not be handed over", error); |
| 34 | return new Response("Docs didn't answer.", { status: 503 }); |
| 35 | } |
| 36 | } |