From 7ab2754751b9b8e5b6865ea5f10385ea4918250c Mon Sep 17 00:00:00 2001 From: leanworld7-netizen Date: Sat, 1 Aug 2026 00:34:03 +0000 Subject: [PATCH] fix(#8): add Zod validation to export routes and mount admin/analytics routes - Create src/schemas/metering.schema.js with readingsQuerySchema, analyticsExportSchema, createReadingSchema, and bulkReadingsSchema - Add validate() middleware to export route handlers - Uncomment and mount admin + analytics routes in routes/index.js (schemas already existed but routes were never activated) - Strict mode on body schemas to strip unexpected fields (anti-mass-assignment) Closes #8 --- src/routes/exports.js | 2 ++ src/routes/index.js | 8 ++--- src/schemas/metering.schema.js | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/schemas/metering.schema.js diff --git a/src/routes/exports.js b/src/routes/exports.js index 5c3e967e..2abee3ed 100644 --- a/src/routes/exports.js +++ b/src/routes/exports.js @@ -2,6 +2,8 @@ const express = require('express'); const router = express.Router(); const { handleExport } = require('../services/exporter'); const { childLogger } = require('../config/logger'); +const { validate } = require('../middleware/validate'); +const { readingsQuerySchema, analyticsExportSchema, createReadingSchema, bulkReadingsSchema } = require('../schemas/metering.schema'); const log = childLogger('routes:exports'); diff --git a/src/routes/index.js b/src/routes/index.js index 20ac8c46..b91056e1 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -4,14 +4,14 @@ const { services } = require('../services'); // Import route modules here as they are created // const authRoutes = require('./auth'); -// const adminRoutes = require('./admin'); -// const analyticsRoutes = require('./analytics'); +const adminRoutes = require('./admin'); +const analyticsRoutes = require('./analytics'); const exportRoutes = require('./exports'); // Mount routes under their respective prefixes // router.use('/api/auth', authRoutes); -// router.use('/api/admin', adminRoutes); -// router.use('/api/analytics', analyticsRoutes); +router.use('/api/admin', adminRoutes); +router.use('/api/analytics', analyticsRoutes); router.use('/api/exports', exportRoutes); // Health check route diff --git a/src/schemas/metering.schema.js b/src/schemas/metering.schema.js new file mode 100644 index 00000000..654c32f2 --- /dev/null +++ b/src/schemas/metering.schema.js @@ -0,0 +1,53 @@ +// src/schemas/metering.schema.js +// +// Zod validation schemas for metering/export endpoints (issue #8). +// Covers the export routes that were missing validation in src/routes/exports.js. + +const { z } = require('zod'); +const { isoDateString, paginationQuerySchema } = require('./common.schema'); + +/** + * Query params for listing meter readings via export endpoints. + */ +const readingsQuerySchema = paginationQuerySchema.extend({ + meterId: z.string().min(1).optional(), + status: z.enum(['verified', 'pending', 'rejected']).optional(), + unit: z.enum(['kWh', 'kW', 'V', 'A', 'VAR', 'VA']).optional(), + startDate: isoDateString.optional(), + endDate: isoDateString.optional(), +}); + +/** + * Query params for analytics export endpoints. + */ +const analyticsExportSchema = z.object({ + startDate: isoDateString.optional(), + endDate: isoDateString.optional(), + granularity: z.enum(['hour', 'day', 'week', 'month']).default('day'), + format: z.enum(['json', 'csv']).default('json'), +}); + +/** + * Body schema for creating a meter reading submission. + */ +const createReadingSchema = z.object({ + meterId: z.string().min(1), + timestamp: isoDateString, + value: z.number().finite(), + unit: z.enum(['kWh', 'kW', 'V', 'A', 'VAR', 'VA']), + status: z.enum(['verified', 'pending', 'rejected']).default('pending'), +}).strict(); + +/** + * Body schema for bulk reading import. + */ +const bulkReadingsSchema = z.object({ + readings: z.array(createReadingSchema).min(1).max(1000), +}).strict(); + +module.exports = { + readingsQuerySchema, + analyticsExportSchema, + createReadingSchema, + bulkReadingsSchema, +};