diff --git a/services/api/openapi.yaml b/services/api/openapi.yaml index 915e77f..dc9e00b 100644 --- a/services/api/openapi.yaml +++ b/services/api/openapi.yaml @@ -564,6 +564,140 @@ paths: "500": $ref: "#/components/responses/ApiError" + /api/blockchain/replay: + post: + tags: [blockchain] + operationId: blockchainReplay + summary: Replay blockchain events (admin) + security: + - ApiKeyAuth: [] + responses: + "200": + description: Replay result + content: + application/json: + schema: + $ref: "#/components/schemas/AnyObject" + "400": + $ref: "#/components/responses/ApiError" + "401": + $ref: "#/components/responses/ApiError" + "403": + $ref: "#/components/responses/ApiError" + "429": + $ref: "#/components/responses/ApiError" + "500": + $ref: "#/components/responses/ApiError" + + /api/v1/email/queue/dead-letter: + get: + tags: [email] + operationId: getEmailDeadLetterList + summary: List dead-letter email jobs (admin) + security: + - ApiKeyAuth: [] + responses: + "200": + description: Dead-letter job list + content: + application/json: + schema: + $ref: "#/components/schemas/AnyObject" + "400": + $ref: "#/components/responses/ApiError" + "401": + $ref: "#/components/responses/ApiError" + "403": + $ref: "#/components/responses/ApiError" + "429": + $ref: "#/components/responses/ApiError" + "500": + $ref: "#/components/responses/ApiError" + + /api/v1/email/queue/dead-letter/{job_id}/requeue: + post: + tags: [email] + operationId: requeueEmailDeadLetterJob + summary: Requeue a dead-letter email job (admin) + security: + - ApiKeyAuth: [] + parameters: + - name: job_id + in: path + required: true + schema: + type: string + description: Dead-letter job identifier + responses: + "200": + description: Requeue result + content: + application/json: + schema: + $ref: "#/components/schemas/AnyObject" + "400": + $ref: "#/components/responses/ApiError" + "401": + $ref: "#/components/responses/ApiError" + "403": + $ref: "#/components/responses/ApiError" + "404": + $ref: "#/components/responses/ApiError" + "429": + $ref: "#/components/responses/ApiError" + "500": + $ref: "#/components/responses/ApiError" + + /api/v1/audit/logs: + get: + tags: [audit] + operationId: getAuditLogs + summary: Retrieve audit log entries (admin) + security: + - ApiKeyAuth: [] + responses: + "200": + description: Audit log entries + content: + application/json: + schema: + $ref: "#/components/schemas/AnyObject" + "400": + $ref: "#/components/responses/ApiError" + "401": + $ref: "#/components/responses/ApiError" + "403": + $ref: "#/components/responses/ApiError" + "429": + $ref: "#/components/responses/ApiError" + "500": + $ref: "#/components/responses/ApiError" + + /api/v1/audit/statistics: + get: + tags: [audit] + operationId: getAuditStatistics + summary: Retrieve audit event statistics (admin) + security: + - ApiKeyAuth: [] + responses: + "200": + description: Audit statistics + content: + application/json: + schema: + $ref: "#/components/schemas/AnyObject" + "400": + $ref: "#/components/responses/ApiError" + "401": + $ref: "#/components/responses/ApiError" + "403": + $ref: "#/components/responses/ApiError" + "429": + $ref: "#/components/responses/ApiError" + "500": + $ref: "#/components/responses/ApiError" + /webhooks/sendgrid: post: tags: [webhooks] diff --git a/services/api/src/config.rs b/services/api/src/config.rs index e545714..6165771 100644 --- a/services/api/src/config.rs +++ b/services/api/src/config.rs @@ -501,6 +501,14 @@ impl Config { errors.push("HMAC_KEY: environment variable is not set or is empty".to_string()); } + // Warn if no API keys are configured — admin endpoints will reject all requests. + if self.api_keys.is_empty() { + eprintln!( + "Warning: API_KEYS is not set. All admin endpoint requests will return 401. \ + Set API_KEYS to a comma-separated list of valid keys." + ); + } + if !errors.is_empty() { for error in &errors { eprintln!("Configuration error: {}", error); diff --git a/services/api/src/security.rs b/services/api/src/security.rs index c98066b..98075d2 100644 --- a/services/api/src/security.rs +++ b/services/api/src/security.rs @@ -301,7 +301,7 @@ impl ApiKeyAuth { } pub fn verify(&self, key: &str) -> bool { - self.valid_keys.is_empty() || self.valid_keys.iter().any(|k| k == key) + !self.valid_keys.is_empty() && self.valid_keys.iter().any(|k| k == key) } }