Public Access
155 lines
4.5 KiB
Rust
155 lines
4.5 KiB
Rust
use crate::database::EntityRepository;
|
|
use axum::{
|
|
Router,
|
|
extract::{Path, State},
|
|
http::StatusCode,
|
|
response::{IntoResponse, Response},
|
|
routing::get,
|
|
};
|
|
use std::sync::Arc;
|
|
use std::time::{Duration, Instant};
|
|
use tokio::sync::RwLock;
|
|
use tracing::{debug, error, info};
|
|
|
|
mod backstage;
|
|
|
|
struct CachedResponse {
|
|
data: String,
|
|
cached_at: Instant,
|
|
}
|
|
|
|
/// State shared across YAML adapter handlers
|
|
#[derive(Clone)]
|
|
pub struct YamlAdapterState {
|
|
pub repository: Arc<EntityRepository>,
|
|
pub base_url: String,
|
|
cache: Arc<RwLock<Option<CachedResponse>>>,
|
|
cache_ttl: Duration,
|
|
}
|
|
|
|
impl YamlAdapterState {
|
|
/// Invalidate the locations cache (for event-based invalidation on entity changes)
|
|
pub async fn invalidate_cache(&self) {
|
|
let mut cache = self.cache.write().await;
|
|
*cache = None;
|
|
}
|
|
}
|
|
|
|
/// Start the YAML adapter HTTP server
|
|
pub async fn start_yaml_adapter(
|
|
host: String,
|
|
port: u16,
|
|
repository: Arc<EntityRepository>,
|
|
base_url: String,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let state = YamlAdapterState {
|
|
repository,
|
|
base_url,
|
|
cache: Arc::new(RwLock::new(None)),
|
|
cache_ttl: Duration::from_secs(30),
|
|
};
|
|
|
|
let app = Router::new()
|
|
.route("/yaml/locations", get(get_locations))
|
|
.route("/yaml/entities/:id", get(get_entity))
|
|
.with_state(state);
|
|
|
|
let addr = format!("{}:{}", host, port);
|
|
info!("Starting YAML adapter HTTP server on {}", addr);
|
|
|
|
let listener = tokio::net::TcpListener::bind(&addr).await?;
|
|
axum::serve(listener, app).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// GET /yaml/locations
|
|
/// Returns a Backstage Location entity listing all entities in Charybdis
|
|
async fn get_locations(State(state): State<YamlAdapterState>) -> Response {
|
|
info!("Received GET /yaml/locations request");
|
|
|
|
// Check cache first
|
|
{
|
|
let cache = state.cache.read().await;
|
|
if let Some(ref cached) = *cache {
|
|
if cached.cached_at.elapsed() < state.cache_ttl {
|
|
debug!("Serving /yaml/locations from cache");
|
|
return (
|
|
StatusCode::OK,
|
|
[("content-type", "text/yaml; charset=utf-8")],
|
|
cached.data.clone(),
|
|
)
|
|
.into_response();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache miss or expired — fetch from database
|
|
match state.repository.list_all().await {
|
|
Ok(entities) => {
|
|
let location_yaml = backstage::create_location_yaml(&entities, &state.base_url);
|
|
|
|
// Update cache
|
|
{
|
|
let mut cache = state.cache.write().await;
|
|
*cache = Some(CachedResponse {
|
|
data: location_yaml.clone(),
|
|
cached_at: Instant::now(),
|
|
});
|
|
}
|
|
|
|
(
|
|
StatusCode::OK,
|
|
[("content-type", "text/yaml; charset=utf-8")],
|
|
location_yaml,
|
|
)
|
|
.into_response()
|
|
}
|
|
Err(e) => {
|
|
error!(error = %e, "Failed to list entities for location");
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
"Failed to retrieve entities",
|
|
)
|
|
.into_response()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// GET /yaml/entities/:id
|
|
/// Returns a specific entity in Backstage YAML format
|
|
async fn get_entity(Path(id): Path<String>, State(state): State<YamlAdapterState>) -> Response {
|
|
info!(entity_id = %id, "Received GET /yaml/entities/:id request");
|
|
|
|
match state.repository.get_by_id(&id).await {
|
|
Ok(Some(entity)) => match backstage::entity_to_yaml(&entity) {
|
|
Ok(yaml) => (
|
|
StatusCode::OK,
|
|
[("content-type", "text/yaml; charset=utf-8")],
|
|
yaml,
|
|
)
|
|
.into_response(),
|
|
Err(e) => {
|
|
error!(entity_id = %id, error = %e, "Failed to convert entity to YAML");
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
"Failed to convert entity to YAML",
|
|
)
|
|
.into_response()
|
|
}
|
|
},
|
|
Ok(None) => {
|
|
info!(entity_id = %id, "Entity not found");
|
|
(StatusCode::NOT_FOUND, "Entity not found").into_response()
|
|
}
|
|
Err(e) => {
|
|
error!(entity_id = %id, error = %e, "Failed to retrieve entity");
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
"Failed to retrieve entity",
|
|
)
|
|
.into_response()
|
|
}
|
|
}
|
|
}
|