The max-100 cap is removed. for all list api
This commit is contained in:
parent
472ccd7ec7
commit
81516a42d0
@ -34,6 +34,6 @@ Stack: Node.js 20+ · Express (CommonJS) · PostgreSQL 15+ · Prisma · Joi · W
|
||||
|
||||
## API
|
||||
|
||||
- Routes under `/api/v1/`; pagination `?page=1&limit=20` (max 100); dates ISO 8601 UTC
|
||||
- Routes under `/api/v1/`; pagination `?page=1&limit=20` (no upper cap on limit); dates ISO 8601 UTC
|
||||
- RBAC actions: `view`, `create`, `edit`, `delete`, `approve`, `export`
|
||||
- Reference: `BACKEND_SETUP.md`, task checklist: `BACKEND_TASKS.md`
|
||||
|
||||
@ -407,7 +407,7 @@ Each sub-master supports: `GET /` (list), `GET /:id`, `POST /`, `PUT /:id`, `DEL
|
||||
## 7. API Conventions
|
||||
|
||||
- Versioning: `/api/v1/...`
|
||||
- Pagination: `?page=1&limit=20` (cap limit at 100); response `meta: { page, limit, total }`
|
||||
- Pagination: `?page=1&limit=20` (limit accepted as sent by FE, min 1, default 20); response `meta: { page, limit, total }`
|
||||
- Filtering: `?status=...&search=...` per module
|
||||
- Sorting: `?sort=-created_at` (`-` = descending)
|
||||
- IDs: `BigInt` serialized as strings in JSON
|
||||
|
||||
@ -113,7 +113,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: table_name
|
||||
schema: { type: string }
|
||||
|
||||
@ -690,7 +690,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -759,7 +759,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -833,7 +833,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -922,7 +922,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -991,7 +991,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1060,7 +1060,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string, description: Search by code or description }
|
||||
@ -1129,7 +1129,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1223,7 +1223,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: type
|
||||
schema: { type: string, enum: [PO, INVOICE] }
|
||||
@ -1295,7 +1295,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1364,7 +1364,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1433,7 +1433,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1513,7 +1513,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1585,7 +1585,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1654,7 +1654,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1723,7 +1723,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1834,7 +1834,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
@ -1929,7 +1929,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
|
||||
@ -187,7 +187,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
|
||||
@ -190,7 +190,7 @@ paths:
|
||||
schema: { type: integer, default: 1 }
|
||||
- in: query
|
||||
name: limit
|
||||
schema: { type: integer, default: 20, maximum: 100 }
|
||||
schema: { type: integer, default: 20, minimum: 1 }
|
||||
- in: query
|
||||
name: search
|
||||
schema: { type: string }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
const {
|
||||
ASSET_CONDITIONS,
|
||||
ASSET_STATUSES,
|
||||
@ -116,8 +117,8 @@ const updateAssetSchema = Joi.object({
|
||||
}).min(1);
|
||||
|
||||
const listAssetsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
status: Joi.string()
|
||||
.valid(...ASSET_STATUSES)
|
||||
@ -315,8 +316,8 @@ const depreciationCalculateSchema = Joi.object({
|
||||
});
|
||||
|
||||
const myMaintenanceAssetsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
due_only: Joi.boolean().optional(),
|
||||
status: Joi.string()
|
||||
@ -334,13 +335,13 @@ const createMaintenanceLogSchema = Joi.object({
|
||||
});
|
||||
|
||||
const listMaintenanceLogsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
});
|
||||
|
||||
const expiryAlertsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
days: Joi.number().integer().min(1).max(365).default(90),
|
||||
type: Joi.string()
|
||||
.valid(...ALERT_TYPES)
|
||||
@ -348,8 +349,8 @@ const expiryAlertsQuerySchema = Joi.object({
|
||||
});
|
||||
|
||||
const serviceAlertsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
status: Joi.string()
|
||||
.valid(...SERVICE_ALERT_STATUSES)
|
||||
.optional(),
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
|
||||
const listAuditLogsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
table_name: Joi.string().max(100).trim().optional(),
|
||||
record_id: Joi.number().integer().positive().optional(),
|
||||
action: Joi.string().max(50).trim().optional(),
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
const { GRN_STATUSES } = require('./grn.constants');
|
||||
|
||||
const grnItemSchema = Joi.object({
|
||||
@ -47,8 +48,8 @@ const updateGrnSchema = Joi.object({
|
||||
}).min(1);
|
||||
|
||||
const listGrnQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
status: Joi.string()
|
||||
.valid(...GRN_STATUSES)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../../utils/pagination.validation');
|
||||
|
||||
const MASTER_CODE_REGEX = /^[A-Za-z0-9\-_/]+$/;
|
||||
const MASTER_NAME_REGEX = /^[A-Za-z0-9 \-_/&.()]+$/;
|
||||
@ -63,8 +64,8 @@ const masterName = ({ max = 150, required = false } = {}) => {
|
||||
};
|
||||
|
||||
const listQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
is_active: Joi.boolean().optional(),
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
const { PO_STATUSES, VENDOR_TYPES } = require('./purchase-orders.constants');
|
||||
|
||||
const poItemSchema = Joi.object({
|
||||
@ -84,8 +85,8 @@ const amendPurchaseOrderSchema = Joi.object({
|
||||
});
|
||||
|
||||
const listPurchaseOrdersQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
status: Joi.string()
|
||||
.valid(...PO_STATUSES)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
const { ASSET_STATUSES } = require('../assets/assets.constants');
|
||||
|
||||
const depreciationReportQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().max(100).trim().allow('').optional(),
|
||||
status: Joi.string()
|
||||
.valid(...ASSET_STATUSES)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
|
||||
const createRoleSchema = Joi.object({
|
||||
name: Joi.string().max(100).required(),
|
||||
@ -38,8 +39,8 @@ const permissionMatrixSchema = Joi.object({
|
||||
});
|
||||
|
||||
const listRolesQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
is_active: Joi.boolean().optional(),
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
|
||||
const roleIdsSchema = Joi.array().items(Joi.number().integer().positive()).min(1);
|
||||
|
||||
@ -33,8 +34,8 @@ const updateUserSchema = Joi.object({
|
||||
}).min(1);
|
||||
|
||||
const listUsersQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
status: Joi.string().valid('active', 'inactive', 'locked').optional(),
|
||||
is_active: Joi.boolean().optional(),
|
||||
|
||||
5
src/modules/vendors/vendors.validation.js
vendored
5
src/modules/vendors/vendors.validation.js
vendored
@ -1,4 +1,5 @@
|
||||
const Joi = require('joi');
|
||||
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
|
||||
const {
|
||||
GST_TREATMENT_VALUES,
|
||||
SOURCE_OF_SUPPLY_VALUES,
|
||||
@ -58,8 +59,8 @@ const vendorStatusSchema = Joi.object({
|
||||
});
|
||||
|
||||
const listVendorsQuerySchema = Joi.object({
|
||||
page: Joi.number().integer().min(1).default(1),
|
||||
limit: Joi.number().integer().min(1).max(100).default(20),
|
||||
page: pageQuery(),
|
||||
limit: limitQuery(),
|
||||
search: Joi.string().allow('').optional(),
|
||||
status: Joi.string()
|
||||
.valid(...vendorStatuses)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const getPagination = (query) => {
|
||||
const page = Math.max(Number(query.page) || 1, 1);
|
||||
const limit = Math.min(Math.max(Number(query.limit) || 20, 1), 100);
|
||||
// Accept whatever positive limit FE sends (no upper cap).
|
||||
const limit = Math.max(Number(query.limit) || 20, 1);
|
||||
const skip = (page - 1) * limit;
|
||||
return { page, limit, skip };
|
||||
};
|
||||
|
||||
14
src/utils/pagination.validation.js
Normal file
14
src/utils/pagination.validation.js
Normal file
@ -0,0 +1,14 @@
|
||||
const Joi = require('joi');
|
||||
|
||||
/** Empty / missing page → 1. */
|
||||
const pageQuery = () =>
|
||||
Joi.number().integer().min(1).empty(['', null]).default(1);
|
||||
|
||||
/**
|
||||
* Empty / missing limit → 20.
|
||||
* When FE sends a number, accept it as-is (no upper cap).
|
||||
*/
|
||||
const limitQuery = () =>
|
||||
Joi.number().integer().min(1).empty(['', null]).default(20);
|
||||
|
||||
module.exports = { pageQuery, limitQuery };
|
||||
Loading…
Reference in New Issue
Block a user