fix(water-log): set wl_admin_session cookie outside pg transaction
Deploy to route.crispygoat.com / deploy (push) Successful in 4m49s

verifyWaterAdminPin previously called cookies().set(...) from inside
the withBrand(...) pg transaction. The pg transaction's
AsyncLocalStorage context masks Next.js's request-scoped cookie
store, so for users without a platform login the cookie set threw
and the route handler returned 500.

Move the cookie write to the outer function frame, after withBrand
resolves. The DB session row is still inserted inside the
transaction (so it's atomic with the PIN verification); only the
cookie write is hoisted out.

The prior fix removed getAdminUser() but left the cookie set inside
the transaction, so valid PINs still 500'd. This is the missing
half.

Adds a static regression test that asserts const cookieStore
is declared AFTER the matching close-paren of withBrand(...).
This commit is contained in:
Tyler
2026-07-01 18:05:09 -06:00
parent 98fc1d3bdd
commit ca79896d5f
2 changed files with 77 additions and 11 deletions
+45
View File
@@ -144,6 +144,51 @@ describe("water-log/settings — verifyWaterAdminPin surface", () => {
/00000000-0000-0000-0000-000000000000/,
);
});
it("writes the wl_admin_session cookie OUTSIDE the withBrand callback", async () => {
// Regression: calling `cookies().set(...)` from inside the
// withBrand(...) pg transaction corrupts Next.js's request-scoped
// AsyncLocalStorage and the cookie set throws for users without a
// platform login (the route handler returns 500). The cookie must
// be written in the OUTER function frame, after withBrand resolves.
//
// Distinguishing OLD vs NEW: in the old code, `const cookieStore
// = await cookies()` was declared INSIDE the withBrand callback
// (between `withBrand(` and its matching closing `)`). In the new
// code, it's declared AFTER that closing. We find the matching
// `)` that closes withBrand (depth-tracked, ignoring string
// contents) and assert that `const cookieStore` comes after it.
const stripped = stripComments(await getVerifyWaterAdminPinSource());
const wbStart = stripped.indexOf("withBrand(");
expect(wbStart).toBeGreaterThan(-1);
const wbOpenParen = wbStart + "withBrand".length; // position of the `(` after `withBrand`
let depth = 1; // we are inside withBrand(
let i = wbOpenParen + 1;
let inString: string | null = null;
let escaped = false;
let wbEnd = -1;
while (i < stripped.length) {
const c = stripped[i];
if (inString) {
if (escaped) { escaped = false; i++; continue; }
if (c === "\\") { escaped = true; i++; continue; }
if (c === inString) inString = null;
i++;
continue;
}
if (c === '"' || c === "'" || c === "`") { inString = c; i++; continue; }
if (c === "(" || c === "{") depth++;
else if (c === ")" || c === "}") {
depth--;
if (depth === 0) { wbEnd = i; break; }
}
i++;
}
expect(wbEnd).toBeGreaterThan(-1);
const cookieStoreIdx = stripped.indexOf("const cookieStore");
expect(cookieStoreIdx).toBeGreaterThan(-1);
expect(cookieStoreIdx).toBeGreaterThan(wbEnd);
});
});
// ── requireFieldSession ─────────────────────────────────────────────────