Rust discount function (one automatic discount evaluating all offers in priority order, parity with the Odoo engine), React Router admin with Polaris, theme app extension and app proxy pages, webhooks incl. GDPR, managed-pricing limits, and a Docker Compose stack (Postgres + Caddy HTTPS) for a single VPS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { PassThrough } from "stream";
|
|
import { renderToPipeableStream } from "react-dom/server";
|
|
import { ServerRouter } from "react-router";
|
|
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
import { type EntryContext } from "react-router";
|
|
import { isbot } from "isbot";
|
|
import { addDocumentResponseHeaders } from "./shopify.server";
|
|
import { startScheduler } from "./lib/scheduler.server";
|
|
|
|
// Re-publishes offer configurations at every start/end boundary (brief §11.5).
|
|
startScheduler();
|
|
|
|
export const streamTimeout = 5000;
|
|
|
|
export default async function handleRequest(
|
|
request: Request,
|
|
responseStatusCode: number,
|
|
responseHeaders: Headers,
|
|
reactRouterContext: EntryContext
|
|
) {
|
|
addDocumentResponseHeaders(request, responseHeaders);
|
|
const userAgent = request.headers.get("user-agent");
|
|
const callbackName = isbot(userAgent ?? '')
|
|
? "onAllReady"
|
|
: "onShellReady";
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const { pipe, abort } = renderToPipeableStream(
|
|
<ServerRouter
|
|
context={reactRouterContext}
|
|
url={request.url}
|
|
/>,
|
|
{
|
|
[callbackName]: () => {
|
|
const body = new PassThrough();
|
|
const stream = createReadableStreamFromReadable(body);
|
|
|
|
responseHeaders.set("Content-Type", "text/html");
|
|
resolve(
|
|
new Response(stream, {
|
|
headers: responseHeaders,
|
|
status: responseStatusCode,
|
|
})
|
|
);
|
|
pipe(body);
|
|
},
|
|
onShellError(error) {
|
|
reject(error);
|
|
},
|
|
onError(error) {
|
|
responseStatusCode = 500;
|
|
console.error(error);
|
|
},
|
|
}
|
|
);
|
|
|
|
// Automatically timeout the React renderer after 6 seconds, which ensures
|
|
// React has enough time to flush down the rejected boundary contents
|
|
setTimeout(abort, streamTimeout + 1000);
|
|
});
|
|
}
|