mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
Merge branch 'fix/v1437-r2-jobs' into release/v1.43.7
This commit is contained in:
commit
e5b7eebe64
62 files changed
+1745
-774
No files matched your search
@@ -14,6 +14,10 @@ vi.mock("@/lib/auth/audit", () => ({
|
||||
auditLog: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() }));
|
||||
vi.mock("@/lib/logging/signal", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/lib/logging/signal")>()),
|
||||
emitSignal: vi.fn(),
|
||||
}));
|
||||
vi.mock("@/lib/db-compat", () => ({
|
||||
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
@@ -78,6 +82,8 @@ vi.mock("@/lib/db", () => ({
|
||||
|
||||
import { POST } from "../route";
|
||||
import { getSession } from "@/lib/auth/session";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { emitSignal } from "@/lib/logging/signal";
|
||||
|
||||
const SESSION_OK = {
|
||||
session: { id: "sess-1", expiresAt: new Date(Date.now() + 3_600_000) },
|
||||
@@ -144,6 +150,18 @@ describe("POST /api/import/apple-health-export", () => {
|
||||
);
|
||||
const res = await POST(req);
|
||||
expect(res.status).toBe(413);
|
||||
// A refused upload is an operational outcome, not an account action: a
|
||||
// signal, never an audit row.
|
||||
expect(auditLog).not.toHaveBeenCalledWith(
|
||||
"import.apple-health.kickoff.denied",
|
||||
expect.anything(),
|
||||
);
|
||||
expect(emitSignal).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: "import.apple-health.kickoff.denied",
|
||||
meta: expect.objectContaining({ reason: "content_length_exceeded" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 429 when the rate-limit window is exhausted", async () => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { apiHandler, requireAuth, HttpError } from "@/lib/api-handler";
|
||||
import { apiError, apiSuccess, getClientIp } from "@/lib/api-response";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { emitSignal } from "@/lib/logging/signal";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
|
||||
import {
|
||||
@@ -77,12 +78,12 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
// content-length cannot bypass it.
|
||||
const declaredBytes = Number(request.headers.get("content-length") ?? 0);
|
||||
if (declaredBytes > MAX_UPLOAD_BYTES) {
|
||||
await auditLog("import.apple-health.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: {
|
||||
emitSignal({
|
||||
action: "import.apple-health.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: {
|
||||
reason: "content_length_exceeded",
|
||||
contentLength: declaredBytes,
|
||||
content_length: declaredBytes,
|
||||
},
|
||||
});
|
||||
return apiError("Upload exceeds 1.5 GB limit", 413);
|
||||
@@ -107,10 +108,10 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "upload_failed";
|
||||
await auditLog("import.apple-health.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { reason: "stream_to_disk_failed", message },
|
||||
emitSignal({
|
||||
action: "import.apple-health.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: { reason: "stream_to_disk_failed", message },
|
||||
});
|
||||
return apiError(`Multipart upload failed: ${message}`, 422);
|
||||
}
|
||||
@@ -150,10 +151,10 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
|
||||
const boss = getGlobalBoss();
|
||||
if (!boss) {
|
||||
await auditLog("import.apple-health.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { reason: "worker_not_running" },
|
||||
emitSignal({
|
||||
action: "import.apple-health.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: { reason: "worker_not_running" },
|
||||
});
|
||||
// Nothing will ever read the staged export; it must not stay in /tmp.
|
||||
await discardStagedUpload(uploaded.filePath);
|
||||
|
||||
@@ -22,6 +22,7 @@ import { apiHandler, requireAuth, HttpError } from "@/lib/api-handler";
|
||||
import { apiError, apiSuccess, getClientIp } from "@/lib/api-response";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { emitSignal } from "@/lib/logging/signal";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
|
||||
import {
|
||||
@@ -67,12 +68,12 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
|
||||
const declaredBytes = Number(request.headers.get("content-length") ?? 0);
|
||||
if (declaredBytes > HEALTH_CONNECT_MAX_UPLOAD_BYTES) {
|
||||
await auditLog("import.health-connect.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: {
|
||||
emitSignal({
|
||||
action: "import.health-connect.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: {
|
||||
reason: "content_length_exceeded",
|
||||
contentLength: declaredBytes,
|
||||
content_length: declaredBytes,
|
||||
},
|
||||
});
|
||||
return apiError("Upload exceeds the 1 GB limit", 413);
|
||||
@@ -94,10 +95,10 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "upload_failed";
|
||||
await auditLog("import.health-connect.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { reason: "stream_to_disk_failed" },
|
||||
emitSignal({
|
||||
action: "import.health-connect.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: { reason: "stream_to_disk_failed" },
|
||||
});
|
||||
return apiError(`Multipart upload failed: ${message}`, 422);
|
||||
}
|
||||
@@ -123,10 +124,10 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
|
||||
const boss = getGlobalBoss();
|
||||
if (!boss) {
|
||||
await auditLog("import.health-connect.kickoff.denied", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { reason: "worker_not_running" },
|
||||
emitSignal({
|
||||
action: "import.health-connect.kickoff.denied",
|
||||
level: "warn",
|
||||
meta: { reason: "worker_not_running" },
|
||||
});
|
||||
await discardStagedUpload(uploaded.filePath);
|
||||
throw new HttpError(503, "Background worker is not running");
|
||||
|
||||
Reference in new issue
Block a user