Public Access
initial-commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user