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
This commit is contained in:
1 parent
2010175a82
commit
bc6983a62e
364 files changed
+10180
-23349
No files matched your search
@@ -1,11 +1,4 @@
|
||||
/**
|
||||
* Authentication middleware for opaque API tokens and Better Auth sessions.
|
||||
*
|
||||
* A bearer value is resolved as a hashed API token first; anything else is
|
||||
* validated as a Better Auth session (cookie or bearer session token).
|
||||
* On success the org_id, role, actor, and credential kind are stored in the
|
||||
* Hono context variables for downstream handlers.
|
||||
*/
|
||||
/** Authentication middleware for opaque API tokens and Better Auth sessions. */
|
||||
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import type { RbacRole } from '../shared/index.js';
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
/**
|
||||
* Global error handler middleware.
|
||||
*
|
||||
* Catches all uncaught exceptions and formats them as a standardized
|
||||
* ErrorResponse with the appropriate HTTP status code, error code,
|
||||
* and the request's unique identifier for tracing.
|
||||
*
|
||||
* When an AppError carries a `messageKey`, the handler resolves it to
|
||||
* the correct language using the `lang` context variable set by the
|
||||
* i18n middleware. This allows services to throw errors with i18n keys
|
||||
* without needing direct access to the Hono context.
|
||||
*/
|
||||
/** Global error handler middleware. */
|
||||
|
||||
import type { ErrorHandler } from 'hono';
|
||||
import type { ErrorCode, ErrorResponse } from '../shared/index.js';
|
||||
@@ -18,18 +7,14 @@ import type { Env, AppVariables } from '../types.js';
|
||||
import { getMessage } from '../i18n/messages.js';
|
||||
import type { Lang } from '../i18n/messages.js';
|
||||
|
||||
/**
|
||||
* Maps each ErrorCode to its corresponding i18n message key.
|
||||
*
|
||||
* This is used when an AppError is thrown with only an error code
|
||||
* (no explicit message or i18n key), so the global error handler
|
||||
* can still resolve a translated default message.
|
||||
*/
|
||||
/** Maps each ErrorCode to its corresponding i18n message key. */
|
||||
const ERROR_CODE_TO_I18N_KEY: Record<ErrorCode, string> = {
|
||||
INVALID_SIGNATURE: 'error.invalidSignature',
|
||||
UNKNOWN_AGENT: 'error.unknownAgent',
|
||||
KEY_REVOKED: 'error.keyRevoked',
|
||||
KEY_RETIRED: 'error.keyRetired',
|
||||
AGENT_FROZEN: 'error.agentFrozen',
|
||||
AGENT_REVOKED: 'error.agentRevoked',
|
||||
TTL_EXPIRED: 'error.ttlExpired',
|
||||
REPLAY_DETECTED: 'error.replayDetected',
|
||||
PREV_HASH_MISMATCH: 'error.prevHashMismatch',
|
||||
@@ -42,16 +27,7 @@ const ERROR_CODE_TO_I18N_KEY: Record<ErrorCode, string> = {
|
||||
VALIDATION_ERROR: 'error.validationError',
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom error class that carries an HTTP status and Elydora error code.
|
||||
*
|
||||
* Supports three modes:
|
||||
* 1. Code-only: `new AppError(status, code)` — uses ErrorCode default message
|
||||
* 2. i18n key: `new AppError(status, code, { key: 'msg.key', params: {...} })` —
|
||||
* the message is resolved at response time from the translation map
|
||||
* 3. Raw string: `new AppError(status, code, 'raw message')` — message is used as-is
|
||||
* (should be avoided in new code; prefer mode 2)
|
||||
*/
|
||||
/** Custom error class that carries an HTTP status and Elydora error code. */
|
||||
export class AppError extends Error {
|
||||
public readonly statusCode: number;
|
||||
public readonly errorCode: ErrorCode;
|
||||
@@ -68,13 +44,13 @@ export class AppError extends Error {
|
||||
details?: Record<string, unknown>,
|
||||
) {
|
||||
if (typeof messageOrOpts === 'object' && messageOrOpts !== null) {
|
||||
// i18n mode — store the key; resolve later in the error handler
|
||||
// i18n mode: store the key; resolve later in the error handler
|
||||
super(getMessage(messageOrOpts.key, 'en', messageOrOpts.params));
|
||||
this.messageKey = messageOrOpts.key;
|
||||
this.messageParams = messageOrOpts.params;
|
||||
this.usesDefaultMessage = false;
|
||||
} else if (messageOrOpts === undefined) {
|
||||
// Code-only mode — will be resolved via ERROR_CODE_TO_I18N_KEY
|
||||
// Code-only mode: will be resolved via ERROR_CODE_TO_I18N_KEY
|
||||
super(ERROR_CODES[errorCode]);
|
||||
this.usesDefaultMessage = true;
|
||||
} else {
|
||||
@@ -88,15 +64,7 @@ export class AppError extends Error {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the user-facing message for a given language.
|
||||
*
|
||||
* Resolution order:
|
||||
* 1. If a messageKey was provided, use that with the translation map.
|
||||
* 2. If the error used the default ErrorCode message, translate via
|
||||
* the ERROR_CODE_TO_I18N_KEY mapping.
|
||||
* 3. Otherwise return the raw message string.
|
||||
*/
|
||||
/** Resolve the user-facing message for a given language. */
|
||||
resolveMessage(lang: Lang): string {
|
||||
if (this.messageKey) {
|
||||
return getMessage(this.messageKey, lang, this.messageParams);
|
||||
@@ -111,12 +79,7 @@ export class AppError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a standardised error response body.
|
||||
*
|
||||
* When `lang` is provided and no explicit `message` is given, the
|
||||
* ErrorCode default message is resolved from the translation map.
|
||||
*/
|
||||
/** Build a standardised error response body. */
|
||||
export function buildErrorResponse(
|
||||
code: ErrorCode,
|
||||
requestId: string,
|
||||
@@ -139,12 +102,7 @@ export function buildErrorResponse(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hono error handler that converts all errors into the ErrorResponse format.
|
||||
*
|
||||
* Resolves i18n message keys using the `lang` context variable set by
|
||||
* the i18n middleware.
|
||||
*/
|
||||
/** Hono error handler that converts all errors into the ErrorResponse format. */
|
||||
export const globalErrorHandler: ErrorHandler<{
|
||||
Bindings: Env;
|
||||
Variables: AppVariables;
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
/**
|
||||
* i18n middleware — language detection from Accept-Language header.
|
||||
*
|
||||
* Reads the Accept-Language request header, detects the preferred
|
||||
* language (English or Chinese), and stores it in the Hono context
|
||||
* so that all downstream handlers and services can access it via
|
||||
* `c.get('lang')`.
|
||||
*/
|
||||
/** i18n middleware: language detection from Accept-Language header. */
|
||||
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import type { Env, AppVariables } from '../types.js';
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
/**
|
||||
* Role-based access control (RBAC) middleware.
|
||||
*
|
||||
* The Elydora console defines five roles with a clear privilege hierarchy:
|
||||
*
|
||||
* org_owner > security_admin > compliance_auditor > readonly_investigator
|
||||
* integration_engineer (parallel to compliance_auditor, agent-management focused)
|
||||
*
|
||||
* This middleware checks whether the role extracted by the auth middleware
|
||||
* satisfies the minimum required role for a given route.
|
||||
*/
|
||||
/** Role-based access control (RBAC) middleware. */
|
||||
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import type { RbacRole } from '../shared/index.js';
|
||||
import type { Env, AppVariables } from '../types.js';
|
||||
import { AppError } from './error-handler.js';
|
||||
|
||||
/**
|
||||
* Privilege level map. Higher number = more privilege.
|
||||
* integration_engineer is a special role with limited admin capabilities
|
||||
* for agent management only.
|
||||
*/
|
||||
/** Privilege level map. */
|
||||
const ROLE_LEVEL: Record<RbacRole, number> = {
|
||||
readonly_investigator: 10,
|
||||
integration_engineer: 20,
|
||||
@@ -28,21 +14,12 @@ const ROLE_LEVEL: Record<RbacRole, number> = {
|
||||
org_owner: 50,
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a role has at least the given privilege level.
|
||||
*/
|
||||
/** Check if a role has at least the given privilege level. */
|
||||
function hasMinimumRole(userRole: RbacRole, requiredRole: RbacRole): boolean {
|
||||
return ROLE_LEVEL[userRole] >= ROLE_LEVEL[requiredRole];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an RBAC middleware that requires a minimum role.
|
||||
*
|
||||
* Usage:
|
||||
* app.post('/v1/agents/:id/freeze', requireRole('security_admin'), handler);
|
||||
*
|
||||
* @param minimumRole - The minimum RBAC role required to access the endpoint
|
||||
*/
|
||||
/** Create an RBAC middleware that requires a minimum role. */
|
||||
export function requireRole(minimumRole: RbacRole): MiddlewareHandler<{
|
||||
Bindings: Env;
|
||||
Variables: AppVariables;
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
/**
|
||||
* Request ID middleware.
|
||||
*
|
||||
* Generates a UUIDv7 for every incoming request and stores it in the
|
||||
* Hono context variables. The ID is also set as the X-Request-Id
|
||||
* response header so clients can reference it when reporting issues.
|
||||
*/
|
||||
/** Request ID middleware. */
|
||||
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import type { Env, AppVariables } from '../types.js';
|
||||
|
||||
Reference in new issue
Block a user