From 01bfefb318560251085aed99f6b4357ba9a66093 Mon Sep 17 00:00:00 2001 From: Ryan Toronto Date: Mon, 30 Sep 2024 01:10:56 +0000 Subject: [PATCH] Request forwarding (#26) --- examples/base-app/CHANGELOG.md | 7 ++ examples/base-app/package.json | 2 +- examples/databases/CHANGELOG.md | 7 ++ examples/databases/package.json | 2 +- examples/kitchen-sink/CHANGELOG.md | 7 ++ examples/kitchen-sink/package.json | 2 +- .../pages/http/request-forwarding.page.tsx | 40 ++++++++++ examples/kitchen-sink/src/pages/nav.tsx | 5 ++ .../async-middleware-not-found.page.tsx | 10 +++ .../src/pages/routing/not-found/layout.tsx | 8 ++ packages/eslint-plugin-twofold/CHANGELOG.md | 2 + packages/eslint-plugin-twofold/package.json | 2 +- packages/framework/CHANGELOG.md | 6 ++ packages/framework/package.json | 2 +- .../framework/src/backend/build/externals.ts | 1 + .../framework/src/backend/build/rsc/rsc.ts | 2 +- packages/framework/src/backend/runtime.ts | 3 + .../src/backend/runtime/api-request.ts | 4 +- .../src/backend/runtime/helpers/request.ts | 15 ++++ .../src/backend/runtime/page-request.ts | 8 +- packages/framework/src/backend/server.ts | 5 ++ .../backend/server/middlewares/dev-reload.ts | 74 +++++++++---------- .../server/middlewares/wait-for-build.ts | 14 ++++ sites/website/CHANGELOG.md | 7 ++ sites/website/package.json | 2 +- todo.md | 3 - 26 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 examples/kitchen-sink/src/pages/http/request-forwarding.page.tsx create mode 100644 examples/kitchen-sink/src/pages/routing/not-found/async-middleware-not-found.page.tsx create mode 100644 packages/framework/src/backend/runtime/helpers/request.ts create mode 100644 packages/framework/src/backend/server/middlewares/wait-for-build.ts diff --git a/examples/base-app/CHANGELOG.md b/examples/base-app/CHANGELOG.md index a429328..ec2ba1c 100644 --- a/examples/base-app/CHANGELOG.md +++ b/examples/base-app/CHANGELOG.md @@ -1,5 +1,12 @@ # base-app +## 0.0.27 + +### Patch Changes + +- Updated dependencies + - @twofold/framework@0.0.28 + ## 0.0.26 ### Patch Changes diff --git a/examples/base-app/package.json b/examples/base-app/package.json index 5554d26..c437418 100644 --- a/examples/base-app/package.json +++ b/examples/base-app/package.json @@ -1,6 +1,6 @@ { "name": "base-app", - "version": "0.0.26", + "version": "0.0.27", "private": true, "description": "", "type": "module", diff --git a/examples/databases/CHANGELOG.md b/examples/databases/CHANGELOG.md index f3c3e27..056306f 100644 --- a/examples/databases/CHANGELOG.md +++ b/examples/databases/CHANGELOG.md @@ -1,5 +1,12 @@ # databases +## 0.0.21 + +### Patch Changes + +- Updated dependencies + - @twofold/framework@0.0.28 + ## 0.0.20 ### Patch Changes diff --git a/examples/databases/package.json b/examples/databases/package.json index afd1a13..e38c962 100644 --- a/examples/databases/package.json +++ b/examples/databases/package.json @@ -1,6 +1,6 @@ { "name": "databases", - "version": "0.0.20", + "version": "0.0.21", "private": true, "description": "", "type": "module", diff --git a/examples/kitchen-sink/CHANGELOG.md b/examples/kitchen-sink/CHANGELOG.md index 1cbe400..b067273 100644 --- a/examples/kitchen-sink/CHANGELOG.md +++ b/examples/kitchen-sink/CHANGELOG.md @@ -1,5 +1,12 @@ # kitchen-sink +## 1.0.27 + +### Patch Changes + +- Updated dependencies + - @twofold/framework@0.0.28 + ## 1.0.26 ### Patch Changes diff --git a/examples/kitchen-sink/package.json b/examples/kitchen-sink/package.json index 3628af4..2ac5c78 100644 --- a/examples/kitchen-sink/package.json +++ b/examples/kitchen-sink/package.json @@ -1,6 +1,6 @@ { "name": "kitchen-sink", - "version": "1.0.26", + "version": "1.0.27", "private": true, "description": "", "type": "module", diff --git a/examples/kitchen-sink/src/pages/http/request-forwarding.page.tsx b/examples/kitchen-sink/src/pages/http/request-forwarding.page.tsx new file mode 100644 index 0000000..e9f23a8 --- /dev/null +++ b/examples/kitchen-sink/src/pages/http/request-forwarding.page.tsx @@ -0,0 +1,40 @@ +import { PageProps } from "@twofold/framework/types"; + +export default function RequestForwardingPage({ request }: PageProps) { + let url = new URL(request.url); + let headers = request.headers; + + return ( +
+

+ Request forwarding +

+

+ Requests passed to pages from proxies and load balancers will have the + correct url, host, and protocol. +

+
+
+
Request URL
+
{request.url}
+
+
+
Forwarded host
+
{headers.get("x-forwarded-host") ?? "None"}
+
+
+
URL host
+
{url.host}
+
+
+
Forwarded protocol
+
{headers.get("x-forwarded-proto") ?? "None"}
+
+
+
URL protocol
+
{url.protocol}
+
+
+
+ ); +} diff --git a/examples/kitchen-sink/src/pages/nav.tsx b/examples/kitchen-sink/src/pages/nav.tsx index 4504338..a728551 100644 --- a/examples/kitchen-sink/src/pages/nav.tsx +++ b/examples/kitchen-sink/src/pages/nav.tsx @@ -167,6 +167,11 @@ export default function Nav() { description="API for lower-level access to the server." href="/http/api" /> + diff --git a/examples/kitchen-sink/src/pages/routing/not-found/async-middleware-not-found.page.tsx b/examples/kitchen-sink/src/pages/routing/not-found/async-middleware-not-found.page.tsx new file mode 100644 index 0000000..87badfe --- /dev/null +++ b/examples/kitchen-sink/src/pages/routing/not-found/async-middleware-not-found.page.tsx @@ -0,0 +1,10 @@ +import { notFound } from "@twofold/framework/not-found"; + +export async function before() { + await new Promise((resolve) => setTimeout(resolve, 1000)); + notFound(); +} + +export default function Page() { + return
You should not see this!
; +} diff --git a/examples/kitchen-sink/src/pages/routing/not-found/layout.tsx b/examples/kitchen-sink/src/pages/routing/not-found/layout.tsx index 9a85a17..12a6c13 100644 --- a/examples/kitchen-sink/src/pages/routing/not-found/layout.tsx +++ b/examples/kitchen-sink/src/pages/routing/not-found/layout.tsx @@ -56,6 +56,14 @@ export default function Layout({ children }: { children: ReactNode }) { Middleware calls notFound +
  • + + Async middleware notFound + +
  • layout.rsc.runMiddleware(props)), ]; - await Promise.all(promises); + return Promise.all(promises); } private notFoundRscResponse() { diff --git a/packages/framework/src/backend/server.ts b/packages/framework/src/backend/server.ts index c25bb15..9f0f40e 100644 --- a/packages/framework/src/backend/server.ts +++ b/packages/framework/src/backend/server.ts @@ -18,6 +18,7 @@ import { pathNormalization } from "./server/middlewares/path-normalization.js"; import { session } from "./server/middlewares/session.js"; import { globalMiddleware } from "./server/middlewares/global-middleware.js"; import { requestStore } from "./server/middlewares/request-store.js"; +import { waitForBuild } from "./server/middlewares/wait-for-build.js"; import { isNotFoundError, isRedirectError, @@ -34,6 +35,10 @@ export async function create(runtime: Runtime) { app.use(cookie()); app.use(await session()); + if (build.env === "development") { + app.use(waitForBuild(build)); + } + app.use(globalMiddleware(build)); app.use(assets(build)); app.use(staticFiles(build)); diff --git a/packages/framework/src/backend/server/middlewares/dev-reload.ts b/packages/framework/src/backend/server/middlewares/dev-reload.ts index 609e3d0..bbce356 100644 --- a/packages/framework/src/backend/server/middlewares/dev-reload.ts +++ b/packages/framework/src/backend/server/middlewares/dev-reload.ts @@ -10,44 +10,40 @@ type Connection = { let activeConnections: Connection[] = []; export function devReload(build: DevBuild): RouteHandler { - if (process.env.NODE_ENV === "production") { - return () => {}; - } else { - return async ({ request }) => { - let url = new URL(request.url); - let pathname = url.pathname; - let method = request.method; - - if (method === "GET" && pathname === "/__dev/reload") { - let handler: () => void; - - return serverSentEvents({ - onOpen(sink) { - handler = () => { - // console.log(build.changes); - sink.sendMessage(JSON.stringify(build.changes)); - }; - - let close = () => { - sink.close(); - build.events.off("complete", handler); - }; - - if (activeConnections.length > 8) { - let connection = activeConnections.shift(); - if (connection) { - connection.close(); - } - } - - activeConnections.push({ close, sink }); - build.events.on("complete", handler); - }, - onClose() { + return async ({ request }) => { + let url = new URL(request.url); + let pathname = url.pathname; + let method = request.method; + + if (method === "GET" && pathname === "/__dev/reload") { + let handler: () => void; + + return serverSentEvents({ + onOpen(sink) { + handler = () => { + // console.log(build.changes); + sink.sendMessage(JSON.stringify(build.changes)); + }; + + let close = () => { + sink.close(); build.events.off("complete", handler); - }, - }); - } - }; - } + }; + + if (activeConnections.length > 8) { + let connection = activeConnections.shift(); + if (connection) { + connection.close(); + } + } + + activeConnections.push({ close, sink }); + build.events.on("complete", handler); + }, + onClose() { + build.events.off("complete", handler); + }, + }); + } + }; } diff --git a/packages/framework/src/backend/server/middlewares/wait-for-build.ts b/packages/framework/src/backend/server/middlewares/wait-for-build.ts new file mode 100644 index 0000000..6a4c554 --- /dev/null +++ b/packages/framework/src/backend/server/middlewares/wait-for-build.ts @@ -0,0 +1,14 @@ +import { RouteHandler } from "@hattip/router"; +import { DevBuild } from "../../build/dev-build"; + +export function waitForBuild(build: DevBuild): RouteHandler { + return async () => { + if (build.isBuilding) { + await new Promise((resolve) => { + build.events.once("complete", () => { + resolve(); + }); + }); + } + }; +} diff --git a/sites/website/CHANGELOG.md b/sites/website/CHANGELOG.md index d266f63..0ae58fe 100644 --- a/sites/website/CHANGELOG.md +++ b/sites/website/CHANGELOG.md @@ -1,5 +1,12 @@ # website +## 0.0.14 + +### Patch Changes + +- Updated dependencies + - @twofold/framework@0.0.28 + ## 0.0.13 ### Patch Changes diff --git a/sites/website/package.json b/sites/website/package.json index b652c9c..f76b3c7 100644 --- a/sites/website/package.json +++ b/sites/website/package.json @@ -1,6 +1,6 @@ { "name": "website", - "version": "0.0.13", + "version": "0.0.14", "private": true, "description": "The twofold website", "type": "module", diff --git a/todo.md b/todo.md index 7e07c8d..1f47db4 100644 --- a/todo.md +++ b/todo.md @@ -2,12 +2,9 @@ framework -- server, hold requests if building - - entries builder should use app defined externals - add --log=debug flag to show debug logs -- add cheerio as external - rename src to app - eslint upgrade