Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
/**
|
|
* Admin/ops API (protected). Policies, key rotation, circuit-breaker.
|
|
* Auth: ADMIN_API_KEY (x-admin-key or admin_key query) or JWT later.
|
|
* Audit: sends to dbis_core central audit when DBIS_CENTRAL_URL + ADMIN_CENTRAL_API_KEY set.
|
|
*/
|
|
|
|
import { Router, Request, Response, NextFunction } from 'express';
|
|
import { setCircuitBreaker } from './observability.js';
|
|
import { appendCentralAudit } from './central-audit.js';
|
|
|
|
const router: Router = Router();
|
|
const ADMIN_API_KEY = process.env.ADMIN_API_KEY;
|
|
|
|
let policies: Record<string, unknown> = {};
|
|
let lastKeyRotationAt: Date | null = null;
|
|
|
|
function getAdminSubject(req: Request): string {
|
|
return (req.headers['x-admin-subject'] as string) || 'multi-chain-execution';
|
|
}
|
|
|
|
function adminAuth(req: Request, res: Response, next: NextFunction): void {
|
|
if (!ADMIN_API_KEY) {
|
|
next();
|
|
return;
|
|
}
|
|
const key = req.headers['x-admin-key'] ?? req.query.admin_key;
|
|
if (key !== ADMIN_API_KEY) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
next();
|
|
}
|
|
|
|
router.use(adminAuth);
|
|
|
|
router.post('/v1/admin/policies', (req: Request, res: Response) => {
|
|
const body = req.body as Record<string, unknown>;
|
|
if (body && typeof body === 'object') {
|
|
policies = { ...policies, ...body };
|
|
}
|
|
appendCentralAudit({
|
|
employeeId: getAdminSubject(req),
|
|
action: 'update_policies',
|
|
permission: 'admin:action',
|
|
resourceType: 'policies',
|
|
metadata: body,
|
|
ipAddress: req.ip || (req.headers['x-forwarded-for'] as string)?.split(',')[0],
|
|
userAgent: req.get('user-agent') ?? undefined,
|
|
}).catch(() => {});
|
|
res.status(200).json({ message: 'Policy update accepted', policies });
|
|
});
|
|
|
|
router.get('/v1/admin/policies', (_req: Request, res: Response) => {
|
|
res.status(200).json({ policies });
|
|
});
|
|
|
|
router.post('/v1/admin/keys/rotate', (req: Request, res: Response) => {
|
|
lastKeyRotationAt = new Date();
|
|
appendCentralAudit({
|
|
employeeId: getAdminSubject(req),
|
|
action: 'keys_rotate',
|
|
permission: 'admin:action',
|
|
resourceType: 'keys',
|
|
ipAddress: req.ip || (req.headers['x-forwarded-for'] as string)?.split(',')[0],
|
|
userAgent: req.get('user-agent') ?? undefined,
|
|
}).catch(() => {});
|
|
res.status(200).json({
|
|
message: 'Key rotation initiated',
|
|
rotated_at: lastKeyRotationAt.toISOString(),
|
|
});
|
|
});
|
|
|
|
router.get('/v1/admin/keys/status', (_req: Request, res: Response) => {
|
|
res.status(200).json({
|
|
last_rotation: lastKeyRotationAt?.toISOString() ?? null,
|
|
});
|
|
});
|
|
|
|
router.post('/v1/admin/circuit-breaker/on', (req: Request, res: Response) => {
|
|
setCircuitBreaker(true);
|
|
appendCentralAudit({
|
|
employeeId: getAdminSubject(req),
|
|
action: 'circuit_breaker_on',
|
|
permission: 'admin:action',
|
|
resourceType: 'circuit_breaker',
|
|
ipAddress: req.ip || (req.headers['x-forwarded-for'] as string)?.split(',')[0],
|
|
userAgent: req.get('user-agent') ?? undefined,
|
|
}).catch(() => {});
|
|
res.status(200).json({ message: 'Circuit breaker forced open' });
|
|
});
|
|
|
|
router.post('/v1/admin/circuit-breaker/off', (req: Request, res: Response) => {
|
|
setCircuitBreaker(false);
|
|
appendCentralAudit({
|
|
employeeId: getAdminSubject(req),
|
|
action: 'circuit_breaker_off',
|
|
permission: 'admin:action',
|
|
resourceType: 'circuit_breaker',
|
|
ipAddress: req.ip || (req.headers['x-forwarded-for'] as string)?.split(',')[0],
|
|
userAgent: req.get('user-agent') ?? undefined,
|
|
}).catch(() => {});
|
|
res.status(200).json({ message: 'Circuit breaker forced closed' });
|
|
});
|
|
|
|
export default router;
|