Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions editor/app/(api)/(public)/v1/submit/[id]/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Env } from "@/env";
import { resend } from "@/clients/resend";
import EmailTemplate from "@/theme/templates-email/formcomplete/default";

const GRIDA_S2S_PRIVATE_API_KEY = process.env.GRIDA_S2S_PRIVATE_API_KEY;
// In hosted env, avoid calling the deployment domain (`*.vercel.app`) since it
// can be protected upstream (401) even when our app routes would allow it.
const HOOK_BASE_URL = Env.server.IS_HOSTED ? Env.web.HOST : Env.server.HOST;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep hook callbacks on the active deployment host

Switching HOOK_BASE_URL to Env.web.HOST in hosted mode can send hook requests to a different environment than the one that processed the submission (for example when NEXT_PUBLIC_URL is a canonical domain but the request is handled by a preview/canary deployment). In that case response_id/form_id lookups happen against the wrong backend and post-submit side effects (session clear, post-indexing, respondent email) are dropped even though submit succeeded. Using the current deployment host (Env.server.HOST) or a deployment-aware fallback avoids cross-environment drift.

Useful? React with 👍 / 👎.

const GRIDA_S2S_PRIVATE_API_KEY = process.env.GRIDA_S2S_PRIVATE_API_KEY ?? null;

const bird = new Bird(
process.env.BIRD_WORKSPACE_ID as string,
Expand All @@ -24,7 +27,7 @@ export namespace OnSubmit {
response_id: string;
session_id: string;
}) {
return fetch(`${Env.server.HOST}/v1/submit/${form_id}/hooks/clearsession`, {
return fetch(`${HOOK_BASE_URL}/v1/submit/${form_id}/hooks/clearsession`, {
headers: {
"Content-Type": "application/json",
},
Expand All @@ -43,7 +46,7 @@ export namespace OnSubmit {
form_id: string;
response_id: string;
}) {
return fetch(`${Env.server.HOST}/v1/submit/${form_id}/hooks/postindexing`, {
return fetch(`${HOOK_BASE_URL}/v1/submit/${form_id}/hooks/postindexing`, {
headers: {
"Content-Type": "application/json",
},
Expand All @@ -62,7 +65,7 @@ export namespace OnSubmit {
response_id: string;
}) {
return fetch(
`${Env.server.HOST}/v1/submit/${form_id}/hooks/notification-respondent-email`,
`${HOOK_BASE_URL}/v1/submit/${form_id}/hooks/notification-respondent-email`,
{
headers: {
"Content-Type": "application/json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@ type Params = { id: string };
* This route uses `service_role` and can send emails, so it must not be
* callable by arbitrary third-parties.
*/
const GRIDA_S2S_PRIVATE_API_KEY = process.env.GRIDA_S2S_PRIVATE_API_KEY;
const GRIDA_S2S_PRIVATE_API_KEY = process.env.GRIDA_S2S_PRIVATE_API_KEY ?? null;

export async function POST(
req: NextRequest,
context: {
params: Promise<Params>;
}
) {
const provided =
req.headers.get("x-grida-s2s-key") ?? req.headers.get("x-hook-secret");
const provided = req.headers.get("x-grida-s2s-key");
if (!GRIDA_S2S_PRIVATE_API_KEY) {
console.error(
"notification-respondent-email/err/misconfigured: GRIDA_S2S_PRIVATE_API_KEY missing"
Expand Down