mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
Merge branch 'release/v1.43.0' into v143/fixui
This commit is contained in:
commit
beb7ef4ccc
93 files changed
+4369
-3956
No files matched your search
@@ -10,6 +10,7 @@ import { redactSecrets } from "@/lib/logging/redact";
|
||||
import type { WideEvent } from "@/lib/logging/types";
|
||||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod/v4";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -26,8 +27,8 @@ const querySchema = z.object({
|
||||
traceId: z.string().trim().min(1).max(200).optional(),
|
||||
level: z.enum(["debug", "info", "warn", "error"]).optional(),
|
||||
action: z.string().trim().min(1).max(200).optional(),
|
||||
since: z.string().datetime().optional(),
|
||||
until: z.string().datetime().optional(),
|
||||
since: isoDateTime({ offset: false }).optional(),
|
||||
until: isoDateTime({ offset: false }).optional(),
|
||||
limit: z.coerce.number().int().min(1).max(PROBLEMS_BUFFER_MAX).default(200),
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { redactSecrets } from "@/lib/logging/redact";
|
||||
import { getAuditLogRetentionDays } from "@/lib/jobs/audit-log-cleanup";
|
||||
import { NextRequest } from "next/server";
|
||||
import { z } from "zod/v4";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
/**
|
||||
* v1.4.16 phase B4: parse the union of legacy + new query params. Legacy
|
||||
@@ -27,8 +28,8 @@ const querySchema = z.object({
|
||||
actor: z.string().trim().min(1).max(200).optional(),
|
||||
action: z.string().trim().min(1).max(200).optional(),
|
||||
target: z.string().trim().min(1).max(200).optional(),
|
||||
since: z.string().datetime().optional(),
|
||||
until: z.string().datetime().optional(),
|
||||
since: isoDateTime({ offset: false }).optional(),
|
||||
until: isoDateTime({ offset: false }).optional(),
|
||||
});
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -41,6 +41,7 @@ import { moodDateKey } from "@/lib/mood/date-key";
|
||||
import { DEFAULT_TIMEZONE } from "@/lib/tz/format";
|
||||
import { deriveA1 } from "@/lib/mood/level-a";
|
||||
import { zonedWallClockToUtc } from "@/lib/tz/wall-clock";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
// Derived from canonical enum so round-trip export → import covers every
|
||||
// type. Previous hardcoded subset silently dropped 4 of 11 types
|
||||
@@ -51,12 +52,12 @@ const measurementSchema = z
|
||||
value: z.number(),
|
||||
unit: z.string(),
|
||||
// v1.17.1 — share the canonical entry-instant bound used by the single
|
||||
// POST + edit paths (v1.17 W1b). Previously a bare `z.string().datetime()`
|
||||
// POST + edit paths (v1.17 W1b). Previously a bare datetime check
|
||||
// with no ceiling, so a crafted JSON import could forward-date a reading.
|
||||
// `validateEntryInstant` rejects any future instant beyond a 5-min skew
|
||||
// tolerance and any instant before 1900; the field transforms to a `Date`.
|
||||
measuredAt: validateEntryInstant(
|
||||
z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
|
||||
isoDateTime().transform((s) => new Date(s)),
|
||||
),
|
||||
glucoseContext: glucoseContextEnum.optional(),
|
||||
source: z.string().optional(),
|
||||
@@ -96,7 +97,7 @@ const moodEntrySchema = z
|
||||
// v1.17.1 — same entry-instant bound as measurements; an imported mood
|
||||
// log cannot be forward-dated. Optional + transforms to a `Date` when set.
|
||||
loggedAt: validateEntryInstant(
|
||||
z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
|
||||
isoDateTime().transform((s) => new Date(s)),
|
||||
).optional(),
|
||||
// v1.12.1 — optional source-stable id (e.g. a Daylio row id). When
|
||||
// present, the import upserts on `(userId, source, externalId)` so a
|
||||
|
||||
@@ -46,6 +46,7 @@ import { annotate } from "@/lib/logging/context";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { persistEcgRecording } from "@/lib/ecg/persist-recording";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -96,7 +97,7 @@ const ingestSourceEnum = z.enum(["APPLE_HEALTH"]);
|
||||
const ecgIngestSchema = z
|
||||
.object({
|
||||
externalRecordingId: z.string().min(1).max(120),
|
||||
recordedAt: z.iso.datetime({ offset: true }),
|
||||
recordedAt: isoDateTime(),
|
||||
// HealthKit hands both of these over as doubles. A watch reports its
|
||||
// average heart rate with a fraction (64.7) often enough that requiring
|
||||
// an integer refused most new recordings while the old ones, synced when
|
||||
|
||||
@@ -112,6 +112,7 @@ import {
|
||||
type MeasurementSource,
|
||||
type MeasurementType,
|
||||
} from "@/generated/prisma/client";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
// v1.4.25 W16c — historical-backfill threshold for PR push
|
||||
// suppression. A batch larger than this fires the detection job with
|
||||
@@ -156,8 +157,8 @@ const batchEntrySchema = z.object({
|
||||
hkIdentifier: z.string().min(1).max(120),
|
||||
value: z.number().finite(),
|
||||
unit: z.string().min(1).max(60),
|
||||
startDate: z.iso.datetime({ offset: true }),
|
||||
endDate: z.iso.datetime({ offset: true }),
|
||||
startDate: isoDateTime(),
|
||||
endDate: isoDateTime(),
|
||||
sleepStage: z.number().int().min(0).max(20).optional(),
|
||||
// v1.10.0 — categorical events (WX-B). The integer `HKCategoryValue`
|
||||
// codepoint for an EVENT-class category sample (irregular-rhythm,
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
import { assertMedicationOwnership } from "@/lib/medications/route-guards";
|
||||
import { dueSchedules } from "@/lib/medications/intake-tracking";
|
||||
import { DEFAULT_TIMEZONE } from "@/lib/tz/format";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
type RouteParams = { params: Promise<{ id: string }> };
|
||||
|
||||
@@ -60,12 +61,10 @@ const RATE_WINDOW_MS = 60_000;
|
||||
|
||||
const querySchema = z
|
||||
.object({
|
||||
from: z.iso
|
||||
.datetime({ offset: true })
|
||||
from: isoDateTime()
|
||||
.transform((s) => new Date(s))
|
||||
.optional(),
|
||||
to: z.iso
|
||||
.datetime({ offset: true })
|
||||
to: isoDateTime()
|
||||
.transform((s) => new Date(s))
|
||||
.optional(),
|
||||
})
|
||||
|
||||
@@ -101,6 +101,7 @@ import {
|
||||
INTAKE_NOT_TRACKED_REASON,
|
||||
isRecordOnly,
|
||||
} from "@/lib/medications/intake-tracking";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
const MAX_ENTRIES_PER_BATCH = 500;
|
||||
const BATCH_RATE_LIMIT_MAX = 60;
|
||||
@@ -109,8 +110,7 @@ const BATCH_RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
||||
const bulkEntrySchema = z
|
||||
.object({
|
||||
medicationId: z.string().min(1),
|
||||
scheduledFor: z.iso
|
||||
.datetime({ offset: true })
|
||||
scheduledFor: isoDateTime()
|
||||
.transform((s) => new Date(s))
|
||||
.optional(),
|
||||
// v1.16.9 — shared plausibility bounds (no future beyond clock skew,
|
||||
@@ -135,8 +135,7 @@ const bulkEntrySchema = z
|
||||
// entry skipped (`force_slot_invalid`) per the bulk per-entry contract.
|
||||
// Ignored on non-taken entries (a pending echo / skip carries no take
|
||||
// to attribute).
|
||||
forceSlotInstant: z.iso
|
||||
.datetime({ offset: true })
|
||||
forceSlotInstant: isoDateTime()
|
||||
.transform((s) => new Date(s))
|
||||
.optional(),
|
||||
// v1.16.4 — per-entry dose override, mirroring the single intake
|
||||
|
||||
@@ -63,6 +63,7 @@ import {
|
||||
droppedLinkKeysForWire,
|
||||
NO_DROPPED_LINK_KEYS,
|
||||
} from "@/lib/mood/tag-links";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
|
||||
const MAX_ENTRIES_PER_BATCH = 500;
|
||||
const BATCH_RATE_LIMIT_MAX = 60;
|
||||
@@ -135,7 +136,7 @@ const bulkEntrySchema = z.object({
|
||||
* @per-entry-parse: moodContextSchema, in the loop below
|
||||
*/
|
||||
context: z.unknown().optional(),
|
||||
moodLoggedAt: z.iso.datetime({ offset: true }).transform((s) => new Date(s)),
|
||||
moodLoggedAt: isoDateTime().transform((s) => new Date(s)),
|
||||
source: moodSourceEnum.optional().default("MANUAL"),
|
||||
/**
|
||||
* Optional iOS-side identifier (e.g. SwiftData row UUID) that lets
|
||||
|
||||
Reference in new issue
Block a user