@upstash/qstash
Version:
Official Typescript client for QStash
178 lines (176 loc) • 5.87 kB
JavaScript
import {
Receiver,
serve
} from "./chunk-RQPZUJXG.mjs";
// platforms/nextjs.ts
var BAD_REQUEST = 400;
function verifySignature(handler, config) {
const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
if (!currentSigningKey) {
throw new Error(
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
);
}
const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
if (!nextSigningKey) {
throw new Error(
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
);
}
const receiver = new Receiver({
currentSigningKey,
nextSigningKey
});
return async (request, response) => {
const signature = request.headers["upstash-signature"];
if (!signature) {
response.status(BAD_REQUEST);
response.send("`Upstash-Signature` header is missing");
response.end();
return;
}
if (typeof signature !== "string") {
throw new TypeError("`Upstash-Signature` header is not a string");
}
const chunks = [];
for await (const chunk of request) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
const body = Buffer.concat(chunks).toString("utf8");
const isValid = await receiver.verify({
signature,
body,
clockTolerance: config?.clockTolerance
});
if (!isValid) {
response.status(BAD_REQUEST);
response.send("Invalid signature");
response.end();
return;
}
try {
request.body = request.headers["content-type"] === "application/json" ? JSON.parse(body) : body;
} catch {
request.body = body;
}
return handler(request, response);
};
}
function verifySignatureEdge(handler, config) {
const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
if (!currentSigningKey) {
throw new Error(
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
);
}
const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
if (!nextSigningKey) {
throw new Error(
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
);
}
const receiver = new Receiver({
currentSigningKey,
nextSigningKey
});
return async (request, nfe) => {
const requestClone = request.clone();
const signature = request.headers.get("upstash-signature");
if (!signature) {
return new Response(new TextEncoder().encode("`Upstash-Signature` header is missing"), {
status: 403
});
}
if (typeof signature !== "string") {
throw new TypeError("`Upstash-Signature` header is not a string");
}
const body = await requestClone.text();
const isValid = await receiver.verify({
signature,
body,
clockTolerance: config?.clockTolerance
});
if (!isValid) {
return new Response(new TextEncoder().encode("invalid signature"), { status: 403 });
}
return handler(request, nfe);
};
}
function verifySignatureAppRouter(handler, config) {
const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
if (!currentSigningKey) {
throw new Error(
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
);
}
const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
if (!nextSigningKey) {
throw new Error(
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
);
}
const receiver = new Receiver({
currentSigningKey,
nextSigningKey
});
return async (request, params) => {
const requestClone = request.clone();
const signature = request.headers.get("upstash-signature");
if (!signature) {
return new Response(new TextEncoder().encode("`Upstash-Signature` header is missing"), {
status: 403
});
}
if (typeof signature !== "string") {
throw new TypeError("`Upstash-Signature` header is not a string");
}
const body = await requestClone.text();
const isValid = await receiver.verify({
signature,
body,
clockTolerance: config?.clockTolerance
});
if (!isValid) {
return new Response(new TextEncoder().encode("invalid signature"), { status: 403 });
}
return handler(request, params);
};
}
var serve2 = (routeFunction, options) => {
const handler = serve(routeFunction, {
onStepFinish: (workflowRunId) => new Response(JSON.stringify({ workflowRunId }), { status: 200 }),
...options
});
return async (request) => {
return await handler(request);
};
};
var servePagesRouter = (routeFunction, options) => {
const handler = serve(routeFunction, options);
return async (req, res) => {
if (req.method?.toUpperCase() !== "POST") {
res.status(405).json("Only POST requests are allowed in worklfows");
return;
} else if (!req.url) {
res.status(400).json("url not found in the request");
return;
}
const protocol = req.headers["x-forwarded-proto"];
const baseUrl = options?.baseUrl ?? `${protocol}://${req.headers.host}`;
const request = new Request(options?.url ?? `${baseUrl}${req.url}`, {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
body: JSON.stringify(req.body) ?? "",
headers: new Headers(req.headersDistinct),
method: "POST"
});
const response = await handler(request);
res.status(response.status).json(await response.json());
};
};
export {
serve2 as serve,
servePagesRouter,
verifySignature,
verifySignatureAppRouter,
verifySignatureEdge
};