diff --git a/website/app/logbook/config.ts b/website/app/logbook/config.ts index 76777df..97b488b 100644 --- a/website/app/logbook/config.ts +++ b/website/app/logbook/config.ts @@ -68,7 +68,7 @@ const voltageSourceSchema = z.object({ bands: z.array(voltageBandSchema).min(1).max(10), }); -export const logbookConfigSchema = z.object({ +export const logbookConfigSchema = z.compile(z.object({ stationary: z .object({ /** A run of fixes staying within this distance of its first fix counts as stopped. */ @@ -138,7 +138,7 @@ export const logbookConfigSchema = z.object({ .default({ noiseFloorMeters: LOGBOOK_DEFAULT_DISTANCE_NOISE_FLOOR_METERS, }), -}); +})); export type LogbookConfig = z.infer; export type LogbookVoltageSource = LogbookConfig["voltage"]["sources"][number]; diff --git a/website/app/routes/api/appUpload.ts b/website/app/routes/api/appUpload.ts index bdd2e64..a9cbe1e 100644 --- a/website/app/routes/api/appUpload.ts +++ b/website/app/routes/api/appUpload.ts @@ -7,25 +7,27 @@ import { getH3IndexForLocation, toUtcDateString } from "~/utils/h3"; export const loader = async ({}: Route.LoaderArgs) => redirect("/"); -const validator = zod.object({ - location: zod.object({ - coords: zod.object({ - accuracy: zod.number(), - longitude: zod.number(), - altitude: zod.number(), - heading: zod.number(), - latitude: zod.number(), - altitudeAccuracy: zod.number(), - speed: zod.number(), +const validator = zod.compile( + zod.object({ + location: zod.object({ + coords: zod.object({ + accuracy: zod.number(), + longitude: zod.number(), + altitude: zod.number(), + heading: zod.number(), + latitude: zod.number(), + altitudeAccuracy: zod.number(), + speed: zod.number(), + }), + mocked: zod.boolean(), + timestamp: zod.number(), + }), + battery: zod.object({ + percentage: zod.number(), + charging: zod.boolean(), }), - mocked: zod.boolean(), - timestamp: zod.number(), - }), - battery: zod.object({ - percentage: zod.number(), - charging: zod.boolean(), }), -}); +); export const action = async ({ context, request }: Route.ActionArgs) => { return data({}, 200); diff --git a/website/app/routes/api/flespiUpload.ts b/website/app/routes/api/flespiUpload.ts index 994cb9f..3c3a279 100644 --- a/website/app/routes/api/flespiUpload.ts +++ b/website/app/routes/api/flespiUpload.ts @@ -8,22 +8,24 @@ import { eq, inArray } from "drizzle-orm"; import type { Route } from "./+types/flespiUpload"; import { getH3IndexForLocation, toUtcDateString } from "~/utils/h3"; -const rawMessageSchema = zod.record(zod.string(), zod.unknown()); - -const normalizedMessageSchema = zod.object({ - timestamp: zod.coerce.number().positive(), - latitude: zod.coerce.number().min(-90).max(90), - longitude: zod.coerce.number().min(-180).max(180), - altitude: zod.coerce.number().optional(), - speed: zod.coerce.number().min(0).optional(), - heading: zod.coerce.number().min(0).max(360).optional(), - accuracy: zod.coerce.number().min(0).optional(), - batteryPercentage: zod.coerce.number().min(0).max(100).optional(), - batteryCharging: zod.coerce.boolean().optional(), - batteryVoltage: zod.coerce.number().min(0).optional(), - identifier: zod.string().min(1), - deviceTypeId: zod.coerce.string().min(1).optional(), -}); +const rawMessageSchema = zod.compile(zod.record(zod.string(), zod.unknown())); + +const normalizedMessageSchema = zod.compile( + zod.object({ + timestamp: zod.coerce.number().positive(), + latitude: zod.coerce.number().min(-90).max(90), + longitude: zod.coerce.number().min(-180).max(180), + altitude: zod.coerce.number().optional(), + speed: zod.coerce.number().min(0).optional(), + heading: zod.coerce.number().min(0).max(360).optional(), + accuracy: zod.coerce.number().min(0).optional(), + batteryPercentage: zod.coerce.number().min(0).max(100).optional(), + batteryCharging: zod.coerce.boolean().optional(), + batteryVoltage: zod.coerce.number().min(0).optional(), + identifier: zod.string().min(1), + deviceTypeId: zod.coerce.string().min(1).optional(), + }), +); const INSERT_CHUNK_SIZE = 200; diff --git a/website/app/routes/api/traccarUpload.ts b/website/app/routes/api/traccarUpload.ts index 0f2e5fc..26dc306 100644 --- a/website/app/routes/api/traccarUpload.ts +++ b/website/app/routes/api/traccarUpload.ts @@ -5,8 +5,8 @@ import { Events } from "~/database/schema/Events"; import type { Route } from "./+types/traccarUpload"; import { getH3IndexForLocation, toUtcDateString } from "~/utils/h3"; -export const loader = async ({ context, request }: Route.LoaderArgs) => { - const getRequestParameters = zod.object({ +const getRequestParameters = zod.compile( + zod.object({ name: zod.string().optional(), uniqueId: zod.string().optional(), status: zod.string().optional(), @@ -25,7 +25,42 @@ export const loader = async ({ context, request }: Route.LoaderArgs) => { address: zod.string().optional(), attributes: zod.string().optional(), gprmc: zod.string().optional(), - }); + }), +); + +const postPayloadSchema = zod.compile( + zod.object({ + event: zod.object({ + id: zod.coerce.number(), + attributes: zod.object({}).optional(), + deviceId: zod.coerce.number(), + type: zod.string(), + eventTime: zod.string(), + positionId: zod.coerce.number(), + geofenceId: zod.coerce.number(), + maintenanceId: zod.coerce.number(), + }), + device: zod.object({ + id: zod.coerce.number(), + attributes: zod.object({}).optional(), + groupId: zod.coerce.number(), + calendarId: zod.coerce.number(), + name: zod.string(), + uniqueId: zod.string(), + status: zod.string(), + lastUpdate: zod.string(), + positionId: zod.coerce.number(), + phone: zod.string().optional(), + model: zod.string().optional(), + contact: zod.string().optional(), + category: zod.string().optional(), + disabled: zod.string(), + expirationTime: zod.string().optional(), + }), + }), +); + +export const loader = async ({ context, request }: Route.LoaderArgs) => { // Get parameters from the request const url = new URL(request.url); console.log( @@ -77,35 +112,6 @@ export const loader = async ({ context, request }: Route.LoaderArgs) => { export const action = async ({ context, request }: Route.ActionArgs) => { if (request.method === "POST") { - const postPayloadSchema = zod.object({ - event: zod.object({ - id: zod.coerce.number(), - attributes: zod.object({}).optional(), - deviceId: zod.coerce.number(), - type: zod.string(), - eventTime: zod.string(), - positionId: zod.coerce.number(), - geofenceId: zod.coerce.number(), - maintenanceId: zod.coerce.number(), - }), - device: zod.object({ - id: zod.coerce.number(), - attributes: zod.object({}).optional(), - groupId: zod.coerce.number(), - calendarId: zod.coerce.number(), - name: zod.string(), - uniqueId: zod.string(), - status: zod.string(), - lastUpdate: zod.string(), - positionId: zod.coerce.number(), - phone: zod.string().optional(), - model: zod.string().optional(), - contact: zod.string().optional(), - category: zod.string().optional(), - disabled: zod.string(), - expirationTime: zod.string().optional(), - }), - }); let payload: unknown; try { payload = await request.json(); diff --git a/website/package-lock.json b/website/package-lock.json index b58d1cf..6f916ce 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -30,7 +30,7 @@ "react-router": "^8.1.0", "recharts": "^3.0.0", "remix-utils": "^10.0.0", - "zod": "^4.4.3", + "zod": "^4.5.2", "zod-form-data": "^2.0.5" }, "devDependencies": { @@ -1717,9 +1717,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1737,9 +1734,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1757,9 +1751,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1777,9 +1768,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1797,9 +1785,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1817,9 +1802,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1837,9 +1819,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1857,9 +1836,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1877,9 +1853,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1903,9 +1876,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1929,9 +1899,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1955,9 +1922,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1981,9 +1945,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2007,9 +1968,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2033,9 +1991,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2059,9 +2014,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2575,9 +2527,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2592,9 +2541,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2609,9 +2555,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2626,9 +2569,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2643,9 +2583,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2660,9 +2597,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2677,9 +2611,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2694,9 +2625,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2711,9 +2639,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2728,9 +2653,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2745,9 +2667,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2762,9 +2681,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2779,9 +2695,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7558,9 +7471,9 @@ } }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.2.tgz", + "integrity": "sha512-XkYXCol10+ba/6F/cueWV+TezUeOqXW0hdeJt5CdXjTYeAgAQg5N03RQdJ80mhfFE72+pblvYMW4wy2Qp4Qbrg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/website/package.json b/website/package.json index 3b7ec87..b7b484b 100644 --- a/website/package.json +++ b/website/package.json @@ -34,9 +34,9 @@ "react-dom": "^19.2.7", "react-leaflet": "^5.0.0", "react-router": "^8.1.0", - "remix-utils": "^10.0.0", "recharts": "^3.0.0", - "zod": "^4.4.3", + "remix-utils": "^10.0.0", + "zod": "^4.5.2", "zod-form-data": "^2.0.5" }, "devDependencies": {