Enhance API services with validation and error handling improvements

- Integrated Zod validation schemas across various API routes to ensure input integrity and improve error handling.
- Updated `mapping-service`, `orchestrator`, `packet-service`, and `webhook-service` to utilize validation middleware for request parameters and bodies.
- Improved error handling in webhook management, packet generation, and compliance routes to provide clearer feedback on request failures.
- Added new validation schemas for various endpoints, enhancing overall API robustness and maintainability.
- Updated dependencies in `package.json` to include the new validation library.
This commit is contained in:
defiQUG
2025-12-12 20:23:45 -08:00
parent d7379f108e
commit e8ae376e90
44 changed files with 2195 additions and 217 deletions

View File

@@ -1,11 +1,18 @@
import { Router } from 'express';
import { z } from 'zod';
import { requireRole } from '../middleware/rbac';
import { validateBody, validateParams } from '@emoney/validation/middleware';
import { bridgeLock, bridgeUnlock, getBridgeLock, getBridgeCorridors } from '../controllers/bridge';
import { bridgeLockSchema, bridgeUnlockSchema } from '@emoney/validation/validators';
export const bridgeRouter = Router();
bridgeRouter.post('/lock', bridgeLock);
bridgeRouter.post('/unlock', requireRole('BRIDGE_OPERATOR'), bridgeUnlock);
bridgeRouter.get('/locks/:lockId', getBridgeLock);
const lockIdParam = z.object({
lockId: z.string().regex(/^[a-fA-F0-9]{64}$/, 'Invalid lock ID'),
});
bridgeRouter.post('/lock', validateBody(bridgeLockSchema), bridgeLock);
bridgeRouter.post('/unlock', requireRole('BRIDGE_OPERATOR'), validateBody(bridgeUnlockSchema), bridgeUnlock);
bridgeRouter.get('/locks/:lockId', validateParams(lockIdParam), getBridgeLock);
bridgeRouter.get('/corridors', getBridgeCorridors);

View File

@@ -1,5 +1,7 @@
import { Router } from 'express';
import { z } from 'zod';
import { requireRole } from '../middleware/rbac';
import { validateBody, validateParams } from '@emoney/validation/middleware';
import {
getComplianceProfile,
setCompliance,
@@ -12,20 +14,82 @@ import {
setWalletTier,
setWalletJurisdictionHash,
} from '../controllers/compliance';
import {
setComplianceSchema,
setFrozenSchema,
setTierSchema,
setJurisdictionHashSchema,
} from '@emoney/validation/validators';
export const complianceRouter = Router();
const accountRefIdParam = z.object({
accountRefId: z.string().regex(/^0x[a-fA-F0-9]{64}$/, 'Invalid account reference ID'),
});
const walletRefIdParam = z.object({
walletRefId: z.string().regex(/^0x[a-fA-F0-9]{64}$/, 'Invalid wallet reference ID'),
});
// Account compliance
complianceRouter.put('/accounts/:accountRefId', requireRole('COMPLIANCE'), setCompliance);
complianceRouter.get('/accounts/:accountRefId', getComplianceProfile);
complianceRouter.put('/accounts/:accountRefId/freeze', requireRole('COMPLIANCE'), setFrozen);
complianceRouter.put('/accounts/:accountRefId/tier', requireRole('COMPLIANCE'), setTier);
complianceRouter.put('/accounts/:accountRefId/jurisdiction', requireRole('COMPLIANCE'), setJurisdictionHash);
complianceRouter.put(
'/accounts/:accountRefId',
requireRole('COMPLIANCE'),
validateParams(accountRefIdParam),
validateBody(setComplianceSchema),
setCompliance
);
complianceRouter.get('/accounts/:accountRefId', validateParams(accountRefIdParam), getComplianceProfile);
complianceRouter.put(
'/accounts/:accountRefId/freeze',
requireRole('COMPLIANCE'),
validateParams(accountRefIdParam),
validateBody(setFrozenSchema),
setFrozen
);
complianceRouter.put(
'/accounts/:accountRefId/tier',
requireRole('COMPLIANCE'),
validateParams(accountRefIdParam),
validateBody(setTierSchema),
setTier
);
complianceRouter.put(
'/accounts/:accountRefId/jurisdiction',
requireRole('COMPLIANCE'),
validateParams(accountRefIdParam),
validateBody(setJurisdictionHashSchema),
setJurisdictionHash
);
// Wallet compliance
complianceRouter.put('/wallets/:walletRefId', requireRole('COMPLIANCE'), setWalletCompliance);
complianceRouter.get('/wallets/:walletRefId', getWalletComplianceProfile);
complianceRouter.put('/wallets/:walletRefId/freeze', requireRole('COMPLIANCE'), setWalletFrozen);
complianceRouter.put('/wallets/:walletRefId/tier', requireRole('COMPLIANCE'), setWalletTier);
complianceRouter.put('/wallets/:walletRefId/jurisdiction', requireRole('COMPLIANCE'), setWalletJurisdictionHash);
complianceRouter.put(
'/wallets/:walletRefId',
requireRole('COMPLIANCE'),
validateParams(walletRefIdParam),
validateBody(setComplianceSchema),
setWalletCompliance
);
complianceRouter.get('/wallets/:walletRefId', validateParams(walletRefIdParam), getWalletComplianceProfile);
complianceRouter.put(
'/wallets/:walletRefId/freeze',
requireRole('COMPLIANCE'),
validateParams(walletRefIdParam),
validateBody(setFrozenSchema),
setWalletFrozen
);
complianceRouter.put(
'/wallets/:walletRefId/tier',
requireRole('COMPLIANCE'),
validateParams(walletRefIdParam),
validateBody(setTierSchema),
setWalletTier
);
complianceRouter.put(
'/wallets/:walletRefId/jurisdiction',
requireRole('COMPLIANCE'),
validateParams(walletRefIdParam),
validateBody(setJurisdictionHashSchema),
setWalletJurisdictionHash
);

View File

@@ -1,9 +1,14 @@
import { Router } from 'express';
import { requireRole } from '../middleware/rbac';
import { validateBody } from '@emoney/validation/middleware';
import { submitInboundMessage, submitOutboundMessage } from '../controllers/iso';
import {
submitInboundMessageSchema,
submitOutboundMessageSchema,
} from '@emoney/validation/validators';
export const isoRouter = Router();
isoRouter.post('/inbound', submitInboundMessage); // mTLS or OAuth2
isoRouter.post('/outbound', submitOutboundMessage);
isoRouter.post('/inbound', validateBody(submitInboundMessageSchema), submitInboundMessage); // mTLS or OAuth2
isoRouter.post('/outbound', validateBody(submitOutboundMessageSchema), submitOutboundMessage);

View File

@@ -1,14 +1,30 @@
import { Router } from 'express';
import { z } from 'zod';
import { requireRole } from '../middleware/rbac';
import { validateBody, validateQuery, validateParams } from '@emoney/validation/middleware';
import { placeLien, listLiens, getLien, reduceLien, releaseLien, getAccountLiens, getEncumbrance } from '../controllers/liens';
import {
placeLienSchema,
reduceLienSchema,
listLiensQuerySchema,
getEncumbranceQuerySchema,
} from '@emoney/validation/validators';
export const liensRouter = Router();
liensRouter.post('/', requireRole('DEBT_AUTHORITY'), placeLien);
liensRouter.get('/', listLiens);
liensRouter.get('/:lienId', getLien);
liensRouter.patch('/:lienId', requireRole('DEBT_AUTHORITY'), reduceLien);
liensRouter.delete('/:lienId', requireRole('DEBT_AUTHORITY'), releaseLien);
liensRouter.get('/accounts/:accountRefId/liens', getAccountLiens);
liensRouter.get('/accounts/:accountRefId/encumbrance', getEncumbrance);
const lienIdParam = z.object({
lienId: z.string().regex(/^[0-9]+$/, 'Invalid lien ID'),
});
const accountRefIdParam = z.object({
accountRefId: z.string().regex(/^0x[a-fA-F0-9]{64}$/, 'Invalid account reference ID'),
});
liensRouter.post('/', requireRole('DEBT_AUTHORITY'), validateBody(placeLienSchema), placeLien);
liensRouter.get('/', validateQuery(listLiensQuerySchema), listLiens);
liensRouter.get('/:lienId', validateParams(lienIdParam), getLien);
liensRouter.patch('/:lienId', requireRole('DEBT_AUTHORITY'), validateParams(lienIdParam), validateBody(reduceLienSchema), reduceLien);
liensRouter.delete('/:lienId', requireRole('DEBT_AUTHORITY'), validateParams(lienIdParam), releaseLien);
liensRouter.get('/accounts/:accountRefId/liens', validateParams(accountRefIdParam), getAccountLiens);
liensRouter.get('/accounts/:accountRefId/encumbrance', validateParams(accountRefIdParam), validateQuery(getEncumbranceQuerySchema), getEncumbrance);

View File

@@ -1,12 +1,32 @@
import { Router } from 'express';
import { z } from 'zod';
import { validateBody, validateParams } from '@emoney/validation/middleware';
import { linkAccountWallet, unlinkAccountWallet, getAccountWallets, getWalletAccounts, connectProvider, getProviderStatus } from '../controllers/mappings';
import {
linkAccountWalletSchema,
unlinkAccountWalletSchema,
connectProviderSchema,
} from '@emoney/validation/validators';
export const mappingsRouter = Router();
mappingsRouter.post('/account-wallet/link', linkAccountWallet);
mappingsRouter.post('/account-wallet/unlink', unlinkAccountWallet);
mappingsRouter.get('/accounts/:accountRefId/wallets', getAccountWallets);
mappingsRouter.get('/wallets/:walletRefId/accounts', getWalletAccounts);
mappingsRouter.post('/providers/:provider/connect', connectProvider);
mappingsRouter.get('/providers/:provider/connections/:connectionId/status', getProviderStatus);
const accountRefIdParam = z.object({
accountRefId: z.string().regex(/^0x[a-fA-F0-9]{64}$/, 'Invalid account reference ID'),
});
const walletRefIdParam = z.object({
walletRefId: z.string().regex(/^0x[a-fA-F0-9]{64}$/, 'Invalid wallet reference ID'),
});
const providerParams = z.object({
provider: z.string().min(1, 'Provider is required'),
connectionId: z.string().min(1, 'Connection ID is required'),
});
mappingsRouter.post('/account-wallet/link', validateBody(linkAccountWalletSchema), linkAccountWallet);
mappingsRouter.post('/account-wallet/unlink', validateBody(unlinkAccountWalletSchema), unlinkAccountWallet);
mappingsRouter.get('/accounts/:accountRefId/wallets', validateParams(accountRefIdParam), getAccountWallets);
mappingsRouter.get('/wallets/:walletRefId/accounts', validateParams(walletRefIdParam), getWalletAccounts);
mappingsRouter.post('/providers/:provider/connect', validateParams(z.object({ provider: z.string().min(1) })), validateBody(connectProviderSchema), connectProvider);
mappingsRouter.get('/providers/:provider/connections/:connectionId/status', validateParams(providerParams), getProviderStatus);

View File

@@ -1,12 +1,24 @@
import { Router } from 'express';
import { z } from 'zod';
import { validateBody, validateQuery, validateParams } from '@emoney/validation/middleware';
import { generatePacket, listPackets, getPacket, downloadPacket, dispatchPacket, acknowledgePacket } from '../controllers/packets';
import {
generatePacketSchema,
dispatchPacketSchema,
acknowledgePacketSchema,
listPacketsQuerySchema,
} from '@emoney/validation/validators';
export const packetsRouter = Router();
packetsRouter.post('/', generatePacket);
packetsRouter.get('/', listPackets);
packetsRouter.get('/:packetId', getPacket);
packetsRouter.get('/:packetId/download', downloadPacket);
packetsRouter.post('/:packetId/dispatch', dispatchPacket);
packetsRouter.post('/:packetId/ack', acknowledgePacket);
const packetIdParam = z.object({
packetId: z.string().regex(/^[a-fA-F0-9]{64}$/, 'Invalid packet ID'),
});
packetsRouter.post('/', validateBody(generatePacketSchema), generatePacket);
packetsRouter.get('/', validateQuery(listPacketsQuerySchema), listPackets);
packetsRouter.get('/:packetId', validateParams(packetIdParam), getPacket);
packetsRouter.get('/:packetId/download', validateParams(packetIdParam), downloadPacket);
packetsRouter.post('/:packetId/dispatch', validateParams(packetIdParam), validateBody(dispatchPacketSchema), dispatchPacket);
packetsRouter.post('/:packetId/ack', validateParams(packetIdParam), validateBody(acknowledgePacketSchema), acknowledgePacket);

View File

@@ -3,21 +3,36 @@
*/
import { Router } from 'express';
import { z } from 'zod';
import { deployToken, listTokens, getToken, updateTokenPolicy } from '../controllers/tokens';
import { mintTokens, burnTokens, clawbackTokens, forceTransferTokens } from '../controllers/tokens';
import { requireRole } from '../middleware/rbac';
import { validateBody, validateQuery, validateParams } from '@emoney/validation/middleware';
import {
deployTokenSchema,
updatePolicySchema,
mintRequestSchema,
burnRequestSchema,
clawbackRequestSchema,
forceTransferRequestSchema,
listTokensQuerySchema,
} from '@emoney/validation/validators';
export const tokensRouter = Router();
const tokenCodeParam = z.object({
code: z.string().regex(/^[A-Z0-9]{1,10}$/, 'Invalid token code'),
});
// Token deployment and management
tokensRouter.post('/', requireRole('TOKEN_DEPLOYER'), deployToken);
tokensRouter.get('/', listTokens);
tokensRouter.get('/:code', getToken);
tokensRouter.patch('/:code/policy', requireRole('POLICY_OPERATOR'), updateTokenPolicy);
tokensRouter.post('/', requireRole('TOKEN_DEPLOYER'), validateBody(deployTokenSchema), deployToken);
tokensRouter.get('/', validateQuery(listTokensQuerySchema), listTokens);
tokensRouter.get('/:code', validateParams(tokenCodeParam), getToken);
tokensRouter.patch('/:code/policy', requireRole('POLICY_OPERATOR'), validateParams(tokenCodeParam), validateBody(updatePolicySchema), updateTokenPolicy);
// Token operations
tokensRouter.post('/:code/mint', requireRole('ISSUER'), mintTokens);
tokensRouter.post('/:code/burn', requireRole('ISSUER'), burnTokens);
tokensRouter.post('/:code/clawback', requireRole('ENFORCEMENT'), clawbackTokens);
tokensRouter.post('/:code/force-transfer', requireRole('ENFORCEMENT'), forceTransferTokens);
tokensRouter.post('/:code/mint', requireRole('ISSUER'), validateParams(tokenCodeParam), validateBody(mintRequestSchema), mintTokens);
tokensRouter.post('/:code/burn', requireRole('ISSUER'), validateParams(tokenCodeParam), validateBody(burnRequestSchema), burnTokens);
tokensRouter.post('/:code/clawback', requireRole('ENFORCEMENT'), validateParams(tokenCodeParam), validateBody(clawbackRequestSchema), clawbackTokens);
tokensRouter.post('/:code/force-transfer', requireRole('ENFORCEMENT'), validateParams(tokenCodeParam), validateBody(forceTransferRequestSchema), forceTransferTokens);

View File

@@ -1,13 +1,24 @@
import { Router } from 'express';
import { z } from 'zod';
import { requireRole } from '../middleware/rbac';
import { validateBody, validateQuery, validateParams } from '@emoney/validation/middleware';
import { listTriggers, getTrigger, validateAndLock, markSubmitted, confirmSettled, confirmRejected } from '../controllers/triggers';
import {
listTriggersQuerySchema,
markSubmittedSchema,
confirmRejectedSchema,
} from '@emoney/validation/validators';
export const triggersRouter = Router();
triggersRouter.get('/', listTriggers);
triggersRouter.get('/:triggerId', getTrigger);
triggersRouter.post('/:triggerId/validate-and-lock', requireRole('POLICY_OPERATOR'), validateAndLock);
triggersRouter.post('/:triggerId/mark-submitted', requireRole('POLICY_OPERATOR'), markSubmitted);
triggersRouter.post('/:triggerId/confirm-settled', requireRole('POLICY_OPERATOR'), confirmSettled);
triggersRouter.post('/:triggerId/confirm-rejected', requireRole('POLICY_OPERATOR'), confirmRejected);
const triggerIdParam = z.object({
triggerId: z.string().regex(/^[a-fA-F0-9]{64}$/, 'Invalid trigger ID'),
});
triggersRouter.get('/', validateQuery(listTriggersQuerySchema), listTriggers);
triggersRouter.get('/:triggerId', validateParams(triggerIdParam), getTrigger);
triggersRouter.post('/:triggerId/validate-and-lock', requireRole('POLICY_OPERATOR'), validateParams(triggerIdParam), validateAndLock);
triggersRouter.post('/:triggerId/mark-submitted', requireRole('POLICY_OPERATOR'), validateParams(triggerIdParam), validateBody(markSubmittedSchema), markSubmitted);
triggersRouter.post('/:triggerId/confirm-settled', requireRole('POLICY_OPERATOR'), validateParams(triggerIdParam), confirmSettled);
triggersRouter.post('/:triggerId/confirm-rejected', requireRole('POLICY_OPERATOR'), validateParams(triggerIdParam), validateBody(confirmRejectedSchema), confirmRejected);