systemprompt_api/routes/gateway/bridge_release/
error.rs1use axum::http::StatusCode;
13use axum::response::{IntoResponse, Response};
14use systemprompt_config::SecretsBootstrapError;
15use systemprompt_loader::ConfigLoadError;
16use systemprompt_models::api::ApiError;
17
18use super::super::bridge_error::BridgeError;
19use crate::error::ApiHttpError;
20
21#[derive(Debug, thiserror::Error)]
22pub enum ReleaseError {
23 #[error(transparent)]
24 Auth(Box<BridgeError>),
25 #[error("services config not ready")]
26 ServicesNotReady(#[source] ConfigLoadError),
27 #[error("bridge releases are not configured on this gateway")]
28 NotConfigured,
29 #[error("no published build for platform {platform}")]
30 UnknownPlatform { platform: String },
31 #[error("no {prefix}* release found in {repo}")]
32 NoRelease { prefix: String, repo: String },
33 #[error("release {tag} has no asset {asset}")]
34 MissingAsset { tag: String, asset: String },
35 #[error("release {version} publishes no SHA256SUMS")]
36 NoChecksums { version: String },
37 #[error("SHA256SUMS has no entry for {asset}")]
38 ChecksumMissing { asset: String },
39 #[error("{stage} failed")]
40 Request {
41 stage: &'static str,
42 #[source]
43 source: reqwest::Error,
44 },
45 #[error("{stage} returned {status}")]
46 UpstreamStatus {
47 stage: &'static str,
48 status: StatusCode,
49 },
50 #[error("bridge release token secret unavailable")]
51 SecretsUnavailable(#[source] SecretsBootstrapError),
52 #[error("bridge release token secret {key} is not configured")]
53 TokenNotConfigured { key: String },
54}
55
56impl ReleaseError {
57 #[must_use]
58 pub const fn status(&self) -> StatusCode {
59 match self {
60 Self::Auth(_) => StatusCode::UNAUTHORIZED,
61 Self::NotConfigured
62 | Self::UnknownPlatform { .. }
63 | Self::NoRelease { .. }
64 | Self::MissingAsset { .. } => StatusCode::NOT_FOUND,
65 Self::NoChecksums { .. }
66 | Self::ChecksumMissing { .. }
67 | Self::Request { .. }
68 | Self::UpstreamStatus { .. } => StatusCode::BAD_GATEWAY,
69 Self::ServicesNotReady(_)
70 | Self::SecretsUnavailable(_)
71 | Self::TokenNotConfigured { .. } => StatusCode::SERVICE_UNAVAILABLE,
72 }
73 }
74
75 #[must_use]
76 pub const fn error_key(&self) -> &'static str {
77 match self {
78 Self::Auth(_) => "invalid_credential",
79 Self::NotConfigured => "bridge_releases_not_configured",
80 Self::UnknownPlatform { .. } => "platform_not_published",
81 Self::NoRelease { .. } => "release_not_found",
82 Self::MissingAsset { .. } => "release_asset_not_found",
83 Self::NoChecksums { .. } | Self::ChecksumMissing { .. } => {
84 "release_checksum_unavailable"
85 },
86 Self::Request { .. } | Self::UpstreamStatus { .. } => "release_upstream_failed",
87 Self::ServicesNotReady(_)
88 | Self::SecretsUnavailable(_)
89 | Self::TokenNotConfigured { .. } => "release_feed_not_ready",
90 }
91 }
92}
93
94impl From<BridgeError> for ReleaseError {
95 fn from(err: BridgeError) -> Self {
96 Self::Auth(Box::new(err))
97 }
98}
99
100impl IntoResponse for ReleaseError {
101 fn into_response(self) -> Response {
102 let status = self.status();
103 let key = self.error_key();
104 let api = match self {
105 Self::Auth(inner) => return ApiHttpError::from(*inner).into_response(),
106 err if status == StatusCode::NOT_FOUND => {
107 ApiError::not_found(err.to_string()).with_error_key(key)
108 },
109 err => ApiError::service_unavailable("Bridge release feed unavailable")
110 .with_error_key(key)
111 .with_source(err),
112 };
113 let mut response = api.into_response();
114 *response.status_mut() = status;
118 response
119 }
120}