Enhance ComboHandler and orchestrator functionality with access control and error handling improvements
- Added AccessControl to ComboHandler for role-based access management. - Implemented gas estimation for plan execution and improved gas limit checks. - Updated execution and preparation methods to enforce step count limits and role restrictions. - Enhanced error handling in orchestrator API endpoints with AppError for better validation feedback. - Integrated request timeout middleware for improved request management. - Updated Swagger documentation to reflect new API structure and parameters.
This commit is contained in:
@@ -3,157 +3,135 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { createHash } from "crypto";
|
||||
import { validatePlan, checkStepDependencies } from "../services/planValidation";
|
||||
import { storePlan, getPlanById, updatePlanSignature } from "../db/plans";
|
||||
import { asyncHandler, AppError, ErrorType } from "../services/errorHandler";
|
||||
import type { Plan, PlanStep } from "../types/plan";
|
||||
|
||||
/**
|
||||
* POST /api/plans
|
||||
* Create a new execution plan
|
||||
* @swagger
|
||||
* /api/plans:
|
||||
* post:
|
||||
* summary: Create a new execution plan
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [creator, steps]
|
||||
* properties:
|
||||
* creator: { type: string }
|
||||
* steps: { type: array }
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Plan created
|
||||
* 400:
|
||||
* description: Validation failed
|
||||
*/
|
||||
export async function createPlan(req: Request, res: Response) {
|
||||
try {
|
||||
const plan: Plan = req.body;
|
||||
|
||||
// Validate plan structure
|
||||
const validation = validatePlan(plan);
|
||||
if (!validation.valid) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid plan",
|
||||
errors: validation.errors,
|
||||
});
|
||||
}
|
||||
|
||||
// Check step dependencies
|
||||
const dependencyCheck = checkStepDependencies(plan.steps);
|
||||
if (!dependencyCheck.valid) {
|
||||
return res.status(400).json({
|
||||
error: "Invalid step dependencies",
|
||||
errors: dependencyCheck.errors,
|
||||
});
|
||||
}
|
||||
|
||||
// Generate plan ID and hash
|
||||
const planId = uuidv4();
|
||||
const planHash = createHash("sha256")
|
||||
.update(JSON.stringify(plan))
|
||||
.digest("hex");
|
||||
|
||||
// Store plan
|
||||
const storedPlan = {
|
||||
...plan,
|
||||
plan_id: planId,
|
||||
plan_hash: planHash,
|
||||
created_at: new Date().toISOString(),
|
||||
status: "pending",
|
||||
};
|
||||
|
||||
await storePlan(storedPlan);
|
||||
|
||||
res.status(201).json({
|
||||
plan_id: planId,
|
||||
plan_hash: planHash,
|
||||
});
|
||||
} catch (error: any) {
|
||||
res.status(500).json({
|
||||
error: "Failed to create plan",
|
||||
message: error.message,
|
||||
});
|
||||
export const createPlan = asyncHandler(async (req: Request, res: Response) => {
|
||||
const plan: Plan = req.body;
|
||||
|
||||
// Validate plan structure
|
||||
const validation = validatePlan(plan);
|
||||
if (!validation.valid) {
|
||||
throw new AppError(ErrorType.VALIDATION_ERROR, 400, "Invalid plan", validation.errors);
|
||||
}
|
||||
}
|
||||
|
||||
// Check step dependencies
|
||||
const dependencyCheck = checkStepDependencies(plan.steps);
|
||||
if (!dependencyCheck.valid) {
|
||||
throw new AppError(ErrorType.VALIDATION_ERROR, 400, "Invalid step dependencies", dependencyCheck.errors);
|
||||
}
|
||||
|
||||
// Generate plan ID and hash
|
||||
const planId = uuidv4();
|
||||
const planHash = createHash("sha256")
|
||||
.update(JSON.stringify(plan))
|
||||
.digest("hex");
|
||||
|
||||
// Store plan
|
||||
const storedPlan = {
|
||||
...plan,
|
||||
plan_id: planId,
|
||||
plan_hash: planHash,
|
||||
created_at: new Date().toISOString(),
|
||||
status: "pending",
|
||||
};
|
||||
|
||||
await storePlan(storedPlan);
|
||||
|
||||
res.status(201).json({
|
||||
plan_id: planId,
|
||||
plan_hash: planHash,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/plans/:planId
|
||||
* Get plan details
|
||||
*/
|
||||
export async function getPlan(req: Request, res: Response) {
|
||||
try {
|
||||
const { planId } = req.params;
|
||||
const plan = await getPlanById(planId);
|
||||
export const getPlan = asyncHandler(async (req: Request, res: Response) => {
|
||||
const { planId } = req.params;
|
||||
const plan = await getPlanById(planId);
|
||||
|
||||
if (!plan) {
|
||||
return res.status(404).json({
|
||||
error: "Plan not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.json(plan);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({
|
||||
error: "Failed to get plan",
|
||||
message: error.message,
|
||||
});
|
||||
if (!plan) {
|
||||
throw new AppError(ErrorType.NOT_FOUND_ERROR, 404, "Plan not found");
|
||||
}
|
||||
}
|
||||
|
||||
res.json(plan);
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/plans/:planId/signature
|
||||
* Add user signature to plan
|
||||
*/
|
||||
export async function addSignature(req: Request, res: Response) {
|
||||
try {
|
||||
const { planId } = req.params;
|
||||
const { signature, messageHash, signerAddress } = req.body;
|
||||
export const addSignature = asyncHandler(async (req: Request, res: Response) => {
|
||||
const { planId } = req.params;
|
||||
const { signature, messageHash, signerAddress } = req.body;
|
||||
|
||||
if (!signature || !messageHash || !signerAddress) {
|
||||
return res.status(400).json({
|
||||
error: "Missing required fields: signature, messageHash, signerAddress",
|
||||
});
|
||||
}
|
||||
|
||||
const plan = await getPlanById(planId);
|
||||
if (!plan) {
|
||||
return res.status(404).json({
|
||||
error: "Plan not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Update plan with signature
|
||||
await updatePlanSignature(planId, {
|
||||
signature,
|
||||
messageHash,
|
||||
signerAddress,
|
||||
signedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
planId,
|
||||
});
|
||||
} catch (error: any) {
|
||||
res.status(500).json({
|
||||
error: "Failed to add signature",
|
||||
message: error.message,
|
||||
});
|
||||
if (!signature || !messageHash || !signerAddress) {
|
||||
throw new AppError(ErrorType.VALIDATION_ERROR, 400, "Missing required fields: signature, messageHash, signerAddress");
|
||||
}
|
||||
}
|
||||
|
||||
const plan = await getPlanById(planId);
|
||||
if (!plan) {
|
||||
throw new AppError(ErrorType.NOT_FOUND_ERROR, 404, "Plan not found");
|
||||
}
|
||||
|
||||
// Update plan with signature
|
||||
await updatePlanSignature(planId, {
|
||||
signature,
|
||||
messageHash,
|
||||
signerAddress,
|
||||
signedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
planId,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/plans/:planId/validate
|
||||
* Validate plan structure and dependencies
|
||||
*/
|
||||
export async function validatePlanEndpoint(req: Request, res: Response) {
|
||||
try {
|
||||
const { planId } = req.params;
|
||||
const plan = await getPlanById(planId);
|
||||
export const validatePlanEndpoint = asyncHandler(async (req: Request, res: Response) => {
|
||||
const { planId } = req.params;
|
||||
const plan = await getPlanById(planId);
|
||||
|
||||
if (!plan) {
|
||||
return res.status(404).json({
|
||||
error: "Plan not found",
|
||||
});
|
||||
}
|
||||
|
||||
const validation = validatePlan(plan);
|
||||
const dependencyCheck = checkStepDependencies(plan.steps);
|
||||
|
||||
res.json({
|
||||
valid: validation.valid && dependencyCheck.valid,
|
||||
validation: validation,
|
||||
dependencies: dependencyCheck,
|
||||
});
|
||||
} catch (error: any) {
|
||||
res.status(500).json({
|
||||
error: "Failed to validate plan",
|
||||
message: error.message,
|
||||
});
|
||||
if (!plan) {
|
||||
throw new AppError(ErrorType.NOT_FOUND_ERROR, 404, "Plan not found");
|
||||
}
|
||||
}
|
||||
|
||||
const validation = validatePlan(plan);
|
||||
const dependencyCheck = checkStepDependencies(plan.steps);
|
||||
|
||||
res.json({
|
||||
valid: validation.valid && dependencyCheck.valid,
|
||||
validation: validation,
|
||||
dependencies: dependencyCheck,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user