Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions services/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
8 changes: 8 additions & 0 deletions services/api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion services/api/src/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Loading