use axum::Json;
use axum::extract::State;
use axum::http::StatusCode;
use chrono::{DateTime, Utc};
use serde::Serialize;
use tracing::info;
use vti_common::auth::{AdminAuth, AuthClaims};
use vti_common::error::AppError;
use crate::community::{CommunityProfile, CommunityProfileUpdate, load_profile, store_profile};
use crate::registry::HealthStatus;
use crate::server::AppState;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CommunityProfileResponse {
#[serde(flatten)]
pub profile: CommunityProfile,
pub registry_status: HealthStatus,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicCommunityProfile {
pub community_did: String,
pub name: String,
pub description: String,
pub logo_url: Option<String>,
pub public_url: Option<String>,
pub contact_email: Option<String>,
pub language: String,
pub created_at: DateTime<Utc>,
pub mediator_did: Option<String>,
}
pub async fn get_public_profile(
State(state): State<AppState>,
) -> Result<Json<PublicCommunityProfile>, AppError> {
let profile = load_profile(&state.community_ks)
.await?
.ok_or_else(|| AppError::NotFound("community profile not initialised".into()))?;
let mediator_did = state
.config
.read()
.await
.messaging
.as_ref()
.map(|m| m.mediator_did.clone());
Ok(Json(PublicCommunityProfile {
community_did: profile.community_did,
name: profile.name,
description: profile.description,
logo_url: profile.logo_url,
public_url: profile.public_url,
contact_email: profile.contact_email,
language: profile.language,
created_at: profile.created_at,
mediator_did,
}))
}
pub async fn get_profile(
_auth: AuthClaims,
State(state): State<AppState>,
) -> Result<Json<CommunityProfileResponse>, AppError> {
let profile = load_profile(&state.community_ks)
.await?
.ok_or_else(|| AppError::NotFound("community profile not initialised".into()))?;
let registry_status = state.registry_health.status().await;
Ok(Json(CommunityProfileResponse {
profile,
registry_status,
}))
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateProfileResponse {
pub profile: CommunityProfile,
pub fields_changed: Vec<String>,
}
pub async fn put_profile(
_admin: AdminAuth,
State(state): State<AppState>,
Json(update): Json<CommunityProfileUpdate>,
) -> Result<(StatusCode, Json<UpdateProfileResponse>), AppError> {
let mut profile = load_profile(&state.community_ks).await?.ok_or_else(|| {
AppError::NotFound("community profile not initialised — cannot PUT before bootstrap".into())
})?;
let fields_changed = update.apply(&mut profile)?;
if fields_changed.is_empty() {
return Ok((
StatusCode::OK,
Json(UpdateProfileResponse {
profile,
fields_changed,
}),
));
}
store_profile(&state.community_ks, &profile).await?;
info!(
community_did = %profile.community_did,
fields_changed = ?fields_changed,
"community profile updated"
);
Ok((
StatusCode::OK,
Json(UpdateProfileResponse {
profile,
fields_changed,
}),
))
}
#[cfg(test)]
mod tests {
}