#![allow(dead_code)]
use axum::{
extract::{Path, State},
response::{Html, Json, IntoResponse},
http::StatusCode,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
use super::AppState;
pub async fn dashboard_handler() -> Html<&'static str> {
Html(include_str!("dashboard.html"))
}
pub async fn get_profile(
State(state): State<Arc<RwLock<AppState>>>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.profile {
Some(profile) => Json(profile).into_response(),
None => (StatusCode::NOT_FOUND, "Profile not found").into_response(),
}
}
pub async fn get_stats(
State(state): State<Arc<RwLock<AppState>>>,
) -> impl IntoResponse {
let state = state.read().await;
let embedder_id = crate::semantic::EmbeddingGenerator::new(
crate::semantic::SemanticConfig::default(),
)
.ok()
.map(|g| (g.id(), g.is_semantic()));
let stats = serde_json::json!({
"has_profile": state.profile.is_some(),
"has_semantic_search": state.semantic_search.is_some(),
"has_ai": state.ai_assistant.is_some(),
"team_count": state.team_profiles.len(),
"embedder": embedder_id.as_ref().map(|(id, _)| id.as_str()),
"embedder_is_semantic": embedder_id.as_ref().map(|(_, sem)| *sem).unwrap_or(false),
});
Json(stats)
}
#[derive(Debug, Deserialize)]
pub struct SearchRequest {
query: String,
#[serde(default = "default_top_k")]
top_k: usize,
language: Option<String>,
}
fn default_top_k() -> usize {
10
}
#[derive(Debug, Serialize)]
pub struct SearchResponse {
results: Vec<SearchResultItem>,
total: usize,
query: String,
}
#[derive(Debug, Serialize)]
pub struct SearchResultItem {
content: String,
language: String,
source_file: String,
repository: String,
score: f32,
highlights: Vec<String>,
}
pub async fn semantic_search(
State(state): State<Arc<RwLock<AppState>>>,
Json(request): Json<SearchRequest>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.semantic_search {
Some(search) => {
let results = match search.search(&request.query, request.top_k).await {
Ok(r) => r,
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
};
let items: Vec<SearchResultItem> = results.into_iter()
.map(|r| SearchResultItem {
content: r.embedding.content,
language: r.embedding.metadata.language,
source_file: r.embedding.metadata.source_file,
repository: r.embedding.metadata.repository,
score: r.score,
highlights: r.highlights,
})
.collect();
let response = SearchResponse {
total: items.len(),
query: request.query,
results: items,
};
Json(response).into_response()
}
None => (StatusCode::SERVICE_UNAVAILABLE, "Semantic search not initialized").into_response(),
}
}
pub async fn search_stats(
State(state): State<Arc<RwLock<AppState>>>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.semantic_search {
Some(search) => {
let stats = search.stats();
Json(stats).into_response()
}
None => (StatusCode::SERVICE_UNAVAILABLE, "Semantic search not initialized").into_response(),
}
}
#[derive(Debug, Deserialize)]
pub struct AskRequest {
question: String,
context: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct AskResponse {
answer: String,
model: String,
}
pub async fn ai_ask(
State(state): State<Arc<RwLock<AppState>>>,
Json(request): Json<AskRequest>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.ai_assistant {
Some(assistant) => {
match assistant.ask(&request.question, None).await {
Ok(answer) => {
let response = AskResponse {
answer,
model: "gpt-4".to_string(), };
Json(response).into_response()
}
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
None => (StatusCode::SERVICE_UNAVAILABLE, "AI assistant not initialized").into_response(),
}
}
#[derive(Debug, Deserialize)]
pub struct ExplainRequest {
code: String,
language: String,
}
pub async fn ai_explain(
State(state): State<Arc<RwLock<AppState>>>,
Json(request): Json<ExplainRequest>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.ai_assistant {
Some(assistant) => {
match assistant.explain_code(&request.code, &request.language).await {
Ok(explanation) => {
Json(serde_json::json!({ "explanation": explanation })).into_response()
}
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
None => (StatusCode::SERVICE_UNAVAILABLE, "AI assistant not initialized").into_response(),
}
}
#[derive(Debug, Deserialize)]
pub struct GenerateRequest {
description: String,
language: String,
}
pub async fn ai_generate(
State(state): State<Arc<RwLock<AppState>>>,
Json(request): Json<GenerateRequest>,
) -> impl IntoResponse {
let state = state.read().await;
match &state.ai_assistant {
Some(assistant) => {
match assistant.generate_code(&request.description, &request.language, None).await {
Ok(code) => {
Json(serde_json::json!({ "code": code })).into_response()
}
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
None => (StatusCode::SERVICE_UNAVAILABLE, "AI assistant not initialized").into_response(),
}
}
pub async fn list_teams(
State(state): State<Arc<RwLock<AppState>>>,
) -> impl IntoResponse {
let state = state.read().await;
let teams: Vec<String> = state.team_profiles.keys().cloned().collect();
Json(teams).into_response()
}
pub async fn get_team(
State(state): State<Arc<RwLock<AppState>>>,
Path(name): Path<String>,
) -> impl IntoResponse {
let state = state.read().await;
match state.team_profiles.get(&name) {
Some(team) => Json(team).into_response(),
None => (StatusCode::NOT_FOUND, format!("Team {} not found", name)).into_response(),
}
}
pub async fn aggregate_team(
State(_state): State<Arc<RwLock<AppState>>>,
Path(name): Path<String>,
) -> impl IntoResponse {
Json(serde_json::json!({ "status": "aggregation started", "team": name })).into_response()
}
pub async fn static_handler(
axum::extract::Path(path): axum::extract::Path<String>,
) -> impl IntoResponse {
match path.as_str() {
"style.css" => (
[("content-type", "text/css")],
include_str!("static/style.css")
).into_response(),
"app.js" => (
[("content-type", "application/javascript")],
include_str!("static/app.js")
).into_response(),
_ => (StatusCode::NOT_FOUND, "Not found").into_response(),
}
}