mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
chore: correct body-cap comments, dockerignore licence posture, stale log copy
- workouts/batch + safeJson: the body cap is a DoS ceiling, not a bound the worst-case payload always fits under; an extreme GPS-heavy backlog can exceed it and falls back to smaller batches client-side. Note that request.text() still buffers the raw body, so the cap bounds the parse cost, not the initial read. - .dockerignore: keep LICENSE / LICENSE.md in the image — AGPL-3.0 conveys with the software. - reminder-worker: the dense walk flag is an operator kill-switch now that the fold is reworked, not a pending-rework gate.
This commit is contained in:
1 parent
33358c7960
commit
8456d3bbc3
4 files changed
+24
-21
No files matched your search
+2
-2
@@ -78,8 +78,8 @@ CONTRIBUTING.md
|
||||
CONTRIBUTING-AI.md
|
||||
CODE_OF_CONDUCT.md
|
||||
SECURITY.md
|
||||
LICENSE.md
|
||||
LICENSE
|
||||
# LICENSE / LICENSE.md are deliberately NOT ignored: AGPL-3.0 conveys with
|
||||
# the software, so the licence text ships inside the image.
|
||||
AGENTS.md
|
||||
.planning
|
||||
docs
|
||||
|
||||
@@ -87,13 +87,13 @@ const PR_DETECTION_SILENT_THRESHOLD = 50;
|
||||
const BATCH_RATE_LIMIT_MAX = 60;
|
||||
const BATCH_RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
||||
|
||||
// 5 MB request-body ceiling. The route geometry is the largest tail in
|
||||
// a workout payload; with the 20 000-point cap on a single LineString
|
||||
// AND the 100-workout cap per batch, the worst-case body is bounded
|
||||
// well below this ceiling for typical lon/lat encodings. A request
|
||||
// above this ceiling almost certainly indicates a misbehaving client
|
||||
// or an attempted DoS — return 413 so the iOS client falls back to
|
||||
// one-workout-per-call.
|
||||
// 5 MB request-body ceiling (pre-existing). The route geometry is the
|
||||
// largest tail in a workout payload; a typical batch sits comfortably
|
||||
// under this, but the 20 000-point LineString cap combined with the
|
||||
// 100-workout batch cap allows a theoretical tens-of-MB body for an
|
||||
// extreme GPS-heavy backlog. Such a body returns 413 so the iOS client
|
||||
// falls back to smaller / one-workout-per-call batches — which is also
|
||||
// the correct response to a misbehaving client or an attempted DoS.
|
||||
const MAX_BODY_BYTES = 5 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
@@ -178,10 +178,10 @@ async function postBatch(request: NextRequest): Promise<Response> {
|
||||
// The Content-Length pre-flight above bounds the common case, but a
|
||||
// chunked upload carries no Content-Length. The `safeJson` body cap
|
||||
// backstops that gap: the raw text is measured before `JSON.parse`,
|
||||
// so an over-limit chunked body returns a clean 413 rather than
|
||||
// buffering an unbounded payload onto the heap. Reuses the same 5 MB
|
||||
// ceiling — the route's 100-workout × 20 000-point worst case sits
|
||||
// well below it, so a real iOS batch is never rejected.
|
||||
// so an over-limit chunked body returns a clean 413 before the parse
|
||||
// cost. Reuses the same 5 MB ceiling — a typical iOS batch sits under
|
||||
// it; an extreme GPS-heavy backlog that exceeds it falls back to
|
||||
// smaller batches client-side.
|
||||
const { data: rawBody, error: jsonError } = await safeJson<unknown>(request, {
|
||||
maxBytes: MAX_BODY_BYTES,
|
||||
});
|
||||
|
||||
+10
-7
@@ -201,13 +201,16 @@ export function apiError(
|
||||
* Returns the parsed body or a 400 error response if parsing fails.
|
||||
*
|
||||
* `opts.maxBytes` opts a route into a hard body-size cap. The check
|
||||
* runs against the raw text rather than the parsed object, so a
|
||||
* gigabyte payload never reaches `JSON.parse` and never lands in
|
||||
* heap. Single-record routes can pass a tight cap (a few KB); batch
|
||||
* routes that legitimately accept large payloads can pass a larger
|
||||
* one (a few hundred KB). When `maxBytes` is omitted the route
|
||||
* inherits the Next.js runtime default — pre-existing behaviour, no
|
||||
* regression risk for callers that haven't adopted the parameter.
|
||||
* runs against the raw text length rather than the parsed object, so an
|
||||
* over-limit payload returns 413 before reaching `JSON.parse` and never
|
||||
* builds an object graph. (`request.text()` still buffers the raw body
|
||||
* into a string, so the cap bounds the parse/object cost, not the
|
||||
* initial read — keep caps comfortably above the largest legitimate
|
||||
* payload.) Single-record routes can pass a tight cap; batch routes
|
||||
* that legitimately accept large payloads pass a larger one. When
|
||||
* `maxBytes` is omitted the route inherits the Next.js runtime default —
|
||||
* pre-existing behaviour, no regression risk for callers that haven't
|
||||
* adopted the parameter.
|
||||
*/
|
||||
export async function safeJson<T = unknown>(
|
||||
request: Request,
|
||||
|
||||
@@ -2824,7 +2824,7 @@ export async function startReminderWorker() {
|
||||
if (!DENSE_INTRADAY_RETENTION_ENABLED) {
|
||||
workerLog(
|
||||
"info",
|
||||
"[dense-intraday-retention] disabled — skipping the nightly walk (P2002 fold collision; re-enable via DENSE_INTRADAY_RETENTION_ENABLED once reworked)",
|
||||
"[dense-intraday-retention] disabled via DENSE_INTRADAY_RETENTION_ENABLED — skipping the nightly walk (operator kill-switch)",
|
||||
);
|
||||
} else {
|
||||
const denseSummary = await runDenseIntradayRetention(
|
||||
|
||||
Reference in new issue
Block a user