Add ECDSA signature verification and enhance ComboHandler functionality

- Integrated ECDSA for signature verification in ComboHandler.
- Updated event emissions to include additional parameters for better tracking.
- Improved gas tracking during execution of combo plans.
- Enhanced database interactions for storing and retrieving plans, including conflict resolution and status updates.
- Added new dependencies for security and database management in orchestrator.
This commit is contained in:
defiQUG
2025-11-05 16:28:48 -08:00
parent 3b09c35c47
commit f600b7b15e
48 changed files with 3381 additions and 46 deletions

View File

@@ -0,0 +1,49 @@
import { Request, Response } from "express";
import { executionCoordinator } from "../services/execution";
import { asyncHandler } from "../services/errorHandler";
import { auditLog } from "../middleware";
/**
* POST /api/plans/:planId/execute
* Execute a plan
*/
export const executePlan = asyncHandler(async (req: Request, res: Response) => {
const { planId } = req.params;
const result = await executionCoordinator.executePlan(planId);
res.json(result);
});
/**
* GET /api/plans/:planId/status
* Get execution status
*/
export const getExecutionStatus = asyncHandler(async (req: Request, res: Response) => {
const { planId } = req.params;
const executionId = req.query.executionId as string;
if (executionId) {
const status = await executionCoordinator.getExecutionStatus(executionId);
return res.json(status);
}
// Get latest execution for plan
res.json({ status: "pending" });
});
/**
* POST /api/plans/:planId/abort
* Abort execution
*/
export const abortExecution = asyncHandler(async (req: Request, res: Response) => {
const { planId } = req.params;
const executionId = req.query.executionId as string;
if (executionId) {
await executionCoordinator.abortExecution(executionId, planId, "User aborted");
}
res.json({ success: true });
});

View File

@@ -0,0 +1,38 @@
import { Router } from "express";
import swaggerUi from "swagger-ui-express";
import swaggerJsdoc from "swagger-jsdoc";
const options: swaggerJsdoc.Options = {
definition: {
openapi: "3.0.0",
info: {
title: "ISO-20022 Combo Flow Orchestrator API",
version: "1.0.0",
description: "API for managing and executing financial workflow plans",
},
servers: [
{
url: "http://localhost:8080",
description: "Development server",
},
],
components: {
securitySchemes: {
ApiKeyAuth: {
type: "apiKey",
in: "header",
name: "X-API-Key",
},
},
},
},
apis: ["./src/api/**/*.ts"],
};
const specs = swaggerJsdoc(options);
export function setupSwagger(router: Router) {
router.use("/api-docs", swaggerUi.serve);
router.get("/api-docs", swaggerUi.setup(specs));
}

View File

@@ -0,0 +1,22 @@
import { Router } from "express";
/**
* API versioning middleware
*/
export function apiVersion(version: string) {
return (req: any, res: any, next: any) => {
req.apiVersion = version;
res.setHeader("API-Version", version);
next();
};
}
/**
* Create versioned router
*/
export function createVersionedRouter(version: string) {
const router = Router();
router.use(apiVersion(version));
return router;
}

View File

@@ -0,0 +1,78 @@
import { Request, Response } from "express";
import { executionCoordinator } from "../services/execution";
import { logger } from "../logging/logger";
interface WebhookConfig {
url: string;
secret: string;
events: string[];
}
const webhooks: Map<string, WebhookConfig> = new Map();
/**
* POST /api/webhooks
* Register a webhook
*/
export async function registerWebhook(req: Request, res: Response) {
try {
const { url, secret, events } = req.body;
if (!url || !secret || !events || !Array.isArray(events)) {
return res.status(400).json({
error: "Invalid webhook configuration",
});
}
const webhookId = `webhook-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
webhooks.set(webhookId, { url, secret, events });
res.json({ webhookId, url, events });
} catch (error: any) {
logger.error({ error }, "Failed to register webhook");
res.status(500).json({ error: error.message });
}
}
/**
* Send webhook notification
*/
export async function sendWebhook(event: string, payload: any) {
for (const [webhookId, config] of webhooks.entries()) {
if (config.events.includes(event) || config.events.includes("*")) {
try {
const signature = createWebhookSignature(JSON.stringify(payload), config.secret);
await fetch(config.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Webhook-Event": event,
"X-Webhook-Signature": signature,
"X-Webhook-Id": webhookId,
},
body: JSON.stringify(payload),
});
} catch (error) {
logger.error({ error, webhookId, event }, "Failed to send webhook");
}
}
}
}
/**
* Create webhook signature
*/
function createWebhookSignature(payload: string, secret: string): string {
const crypto = require("crypto");
return crypto.createHmac("sha256", secret).update(payload).digest("hex");
}
// Listen to execution events
executionCoordinator.onStatus((executionId, event) => {
sendWebhook("plan.status", {
executionId,
...event,
});
});