fix(water-log): set wl_admin_session cookie outside pg transaction
Deploy to route.crispygoat.com / deploy (push) Successful in 4m49s
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:
@@ -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 ─────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user