Files
Elydora-Open-Source/packages/server/src/routes/operations.ts
T
雷电芽衣 bc6983a62e chore(oss): audit the server package and resync the SDK mirrors
Split the 657-line operation service into eor, submit, and verify
modules, add the AGENT_REVOKED and KEY_RETIRED codes the Backend already
emits, and condense every multi-line comment. Mirror Node, Python, and
Go at 2.1.0 with the deep-health 503, non-idempotent retry, rotation
grace, and path-encoding fixes. Document which routes and response
fields the self-hosted server does not provide.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
2026-09-03 20:48:20 -04:00

89 lines
2.9 KiB
TypeScript

/** Operation routes: submit, retrieve, and verify signed operation records. */
import { Hono } from 'hono';
import type { Env, AppVariables } from '../types.js';
import type { EOR } from '../shared/index.js';
import { authMiddleware } from '../middleware/auth.js';
import { requireRole } from '../middleware/rbac.js';
import { AppError } from '../middleware/error-handler.js';
import * as operationService from '../services/operation-service.js';
const operations = new Hono<{ Bindings: Env; Variables: AppVariables }>();
// ---------------------------------------------------------------------------
// POST /v1/operations: Submit a signed EOR
// ---------------------------------------------------------------------------
// Operation submission uses the EOR's embedded signature for authentication
// rather than a Bearer token. The agent authenticates via its Ed25519 signature.
// We still apply auth middleware to get the org context.
operations.post(
'/',
authMiddleware,
requireRole('integration_engineer'),
async (c) => {
const eorBody = await c.req.json<EOR>();
const orgId = c.get('org_id');
const eor = { ...eorBody, org_id: orgId };
if (!eor || !eor.operation_id) {
throw new AppError(400, 'VALIDATION_ERROR', { key: 'operation.invalidBody' });
}
const result = await operationService.submitOperation(
c.env.ELYDORA_DB,
c.env.ELYDORA_EVIDENCE,
c.env.ELYDORA_CACHE,
c.env.ELYDORA_QUEUE,
eor,
c.env.ELYDORA_SIGNING_KEY,
);
c.header('X-Elydora-Protocol-Version', c.env.PROTOCOL_VERSION);
return c.json(result, 202);
},
);
// ---------------------------------------------------------------------------
// GET /v1/operations/:operation_id: Retrieve an operation
// ---------------------------------------------------------------------------
operations.get(
'/:operation_id',
authMiddleware,
requireRole('readonly_investigator'),
async (c) => {
const operationId = c.req.param('operation_id');
const orgId = c.get('org_id');
const result = await operationService.getOperation(c.env.ELYDORA_DB, c.env.ELYDORA_EVIDENCE, operationId, orgId);
c.header('X-Elydora-Protocol-Version', c.env.PROTOCOL_VERSION);
return c.json(result, 200);
},
);
// ---------------------------------------------------------------------------
// POST /v1/operations/:operation_id/verify: Verify operation integrity
// ---------------------------------------------------------------------------
operations.post(
'/:operation_id/verify',
authMiddleware,
requireRole('readonly_investigator'),
async (c) => {
const operationId = c.req.param('operation_id');
const orgId = c.get('org_id');
const result = await operationService.verifyOperation(
c.env.ELYDORA_DB,
c.env.ELYDORA_EVIDENCE,
operationId,
orgId,
);
c.header('X-Elydora-Protocol-Version', c.env.PROTOCOL_VERSION);
return c.json(result, 200);
},
);
export { operations };