mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(rate-limit): fail the shared write ceiling open and skip reads over POST
The shared write bucket threw on any SQL error, so a lock on rate_limits answered every authenticated write with 500. It is a loop cutter, not an access check: when the bucket cannot be read the write now goes ahead with a warn signal. The login and other auth-surface limits call the limiter directly and still fail closed. requireAuth now passes readOverPost to the meter, the draft-only document assist declares it, and the three chat routes, which meter their turns with buckets of their own, declare that instead of being counted twice.
This commit is contained in:
1 parent
408ae35b7c
commit
5185edca74
12 files changed
+148
-19
No files matched your search
@@ -71,7 +71,9 @@ type RouteParams = { params: Promise<{ id: string }> };
|
||||
|
||||
export const POST = apiHandler(
|
||||
async (request: NextRequest, { params }: RouteParams) => {
|
||||
const { user } = await requireAuth();
|
||||
// Metered by the document chat's own bucket below, not the shared
|
||||
// ceiling.
|
||||
const { user } = await requireAuth(undefined, { writeBucket: "own" });
|
||||
const userId = user.id;
|
||||
|
||||
const gate = await requireModuleEnabled(userId, "inboundDocuments");
|
||||
|
||||
@@ -73,7 +73,9 @@ type RouteParams = { params: Promise<{ id: string }> };
|
||||
|
||||
export const POST = apiHandler(
|
||||
async (request: NextRequest, { params }: RouteParams) => {
|
||||
const { user } = await requireAuth();
|
||||
// Writes nothing (the draft goes back to the edit form), so the shared
|
||||
// write ceiling does not count it; the document-AI bucket meters it.
|
||||
const { user } = await requireAuth(undefined, { readOverPost: true });
|
||||
|
||||
const gate = await requireModuleEnabled(user.id, "inboundDocuments");
|
||||
if (!gate.enabled) return gate.response;
|
||||
|
||||
@@ -52,7 +52,8 @@ export const dynamic = "force-dynamic";
|
||||
export const runtime = "nodejs";
|
||||
|
||||
export const POST = apiHandler(async (request: NextRequest) => {
|
||||
const { user } = await requireAuth();
|
||||
// Metered by the fenced chat's own bucket below, not the shared ceiling.
|
||||
const { user } = await requireAuth(undefined, { writeBucket: "own" });
|
||||
const userId = user.id;
|
||||
|
||||
const gate = await requireModuleEnabled(userId, "inboundDocuments");
|
||||
|
||||
@@ -81,7 +81,9 @@ const COACH_CONCURRENT_TURNS = 2;
|
||||
const COACH_TURN_SLOT_MS = TURN_LIMITS.user.wallMs + 30_000;
|
||||
|
||||
async function handleChatRequest(request: NextRequest): Promise<Response> {
|
||||
const auth = await requireAuth();
|
||||
// A turn is metered by the Coach's own buckets (`coach-chat`, the turn
|
||||
// slot), not the shared write ceiling.
|
||||
const auth = await requireAuth(undefined, { writeBucket: "own" });
|
||||
// The `coach` capability, right after auth and before the rate limit and
|
||||
// the body parse. It folds in what the Coach module gate, the operator
|
||||
// switch and the `disableCoach` opt-out used to answer separately, plus
|
||||
|
||||
Reference in new issue
Block a user