use std::sync::Arc;
use axum::{
extract::{Path, Query, State, WebSocketUpgrade},
http::{HeaderMap, StatusCode},
response::IntoResponse,
routing::{delete, get, post},
Json, Router,
};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast;
use tower_http::cors::CorsLayer;
use tracing::{error, info};
use crate::domain::invitation_code::InvitationCode;
use crate::domain::user::User;
use crate::domain::{Agent, LLMConfig, Message, MessageTarget, Organization, Role};
use crate::infrastructure::auth::{JwtService, PasswordService, UserInfo};
#[derive(Serialize)]
struct ErrorResponse {
error: String,
}
#[derive(Clone)]
pub struct AppState {
pub agents: Vec<Agent>,
pub message_tx: broadcast::Sender<Message>,
pub store: Arc<dyn crate::core::store::Store>,
pub jwt_service: JwtService,
}
#[derive(Serialize)]
pub struct AgentResponse {
pub id: String,
pub name: String,
pub role: String,
pub department: String,
pub description: Option<String>,
}
#[derive(Deserialize)]
pub struct SendMessageRequest {
pub from: String,
pub to: Option<String>,
pub content: String,
}
#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ClientMessage {
#[serde(rename = "send_message")]
SendMessage {
from: String,
to: String,
content: String,
},
#[serde(rename = "ping")]
Ping,
}
#[allow(dead_code)]
async fn validate_jwt(state: &AppState, token: &str) -> Option<UserInfo> {
state.jwt_service.validate_token(token).ok()
}
async fn get_current_user(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> impl IntoResponse {
let auth_header = headers
.get("authorization")
.and_then(|value| value.to_str().ok());
if let Some(auth_str) = auth_header {
if auth_str.starts_with("Bearer ") {
let token = auth_str.strip_prefix("Bearer ").unwrap();
if let Ok(user_info) = state.jwt_service.validate_token(token) {
return Json(serde_json::json!({
"success": true,
"data": {
"id": user_info.id,
"username": user_info.username,
"name": user_info.name,
"email": user_info.email,
"is_director": user_info.is_director,
"employee_id": user_info.employee_id,
"position": user_info.position,
"department": user_info.department,
}
}))
.into_response();
}
}
}
(
StatusCode::UNAUTHORIZED,
Json(ErrorResponse {
error: "Unauthorized".to_string(),
}),
)
.into_response()
}
#[derive(Deserialize)]
pub struct AuthRequest {
pub username: String,
pub password: String,
}
#[derive(Deserialize)]
pub struct RegisterRequest {
pub username: String,
pub password: String,
pub name: String,
pub email: Option<String>,
pub invite_code: Option<String>, }
async fn health_check() -> impl IntoResponse {
Json(serde_json::json!({
"status": "ok",
"timestamp": Utc::now().to_rfc3339(),
}))
}
async fn list_agents(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let agents: Vec<AgentResponse> = state
.agents
.iter()
.map(|a| AgentResponse {
id: a.id.clone(),
name: a.name.clone(),
role: a.role.title.clone(),
department: a.department_id.clone().unwrap_or_default(),
description: a.description.clone(),
})
.collect();
Json(agents)
}
async fn get_agent(
State(state): State<Arc<AppState>>,
Path(agent_id): Path<String>,
) -> impl IntoResponse {
match state.agents.iter().find(|a| a.id == agent_id) {
Some(agent) => Json(serde_json::json!({
"id": agent.id,
"name": agent.name,
"role": agent.role.title,
"department": agent.department_id,
"description": agent.description,
}))
.into_response(),
None => (
StatusCode::NOT_FOUND,
Json(ErrorResponse {
error: format!("Agent not found: {}", agent_id),
}),
)
.into_response(),
}
}
async fn get_company(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let departments: Vec<String> = state
.agents
.iter()
.filter_map(|a| a.department_id.clone())
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
Json(serde_json::json!({
"name": "ImitatorT Virtual Company",
"agent_count": state.agents.len(),
"departments": departments,
}))
}
async fn send_message(
State(state): State<Arc<AppState>>,
Json(req): Json<SendMessageRequest>,
) -> impl IntoResponse {
let to = if let Some(to_id) = req.to {
MessageTarget::Direct(to_id)
} else {
return (
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "Missing 'to' field".to_string(),
}),
)
.into_response();
};
let message = Message {
id: uuid::Uuid::new_v4().to_string(),
from: req.from,
to,
content: req.content,
timestamp: Utc::now().timestamp(),
reply_to: None,
mentions: Vec::new(),
};
let _ = state.message_tx.send(message.clone());
Json(serde_json::json!({
"id": message.id,
"status": "sent",
"timestamp": message.timestamp,
}))
.into_response()
}
async fn login(
State(state): State<Arc<AppState>>,
Json(req): Json<AuthRequest>,
) -> impl IntoResponse {
info!("Login attempt: {}", req.username);
match state.store.load_user_by_username(&req.username).await {
Ok(Some(user)) => {
match PasswordService::verify_password(&user.password_hash, &req.password) {
Ok(valid) if valid => {
let token: String = match state.jwt_service.generate_token(&UserInfo {
id: user.id.clone(),
username: user.username.clone(),
name: user.name.clone(),
email: user.email.clone(),
is_director: matches!(
user.position,
crate::domain::user::Position::Chairman
| crate::domain::user::Position::Management
),
employee_id: user.employee_id.clone(),
position: format!("{:?}", user.position),
department: user.department.clone(),
}) {
Ok(token) => token,
Err(e) => {
error!("Failed to generate token: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to generate token".to_string(),
}),
)
.into_response();
}
};
Json(serde_json::json!({
"success": true,
"data": {
"token": token,
"user": {
"id": user.id,
"username": user.username,
"name": user.name,
"email": user.email,
"is_director": matches!(user.position, crate::domain::user::Position::Chairman | crate::domain::user::Position::Management),
"employee_id": user.employee_id,
"position": format!("{:?}", user.position),
"department": user.department,
}
}
})).into_response()
}
Ok(_) | Err(_) => {
(
StatusCode::UNAUTHORIZED,
Json(ErrorResponse {
error: "Invalid username or password".to_string(),
}),
)
.into_response()
}
}
}
Ok(None) => {
(
StatusCode::UNAUTHORIZED,
Json(ErrorResponse {
error: "Invalid username or password".to_string(),
}),
)
.into_response()
}
Err(e) => {
error!("Database error during login: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Database error".to_string(),
}),
)
.into_response()
}
}
}
async fn register(
State(state): State<Arc<AppState>>,
Json(req): Json<RegisterRequest>,
) -> impl IntoResponse {
info!("Register attempt: {}", req.username);
match state.store.load_user_by_username(&req.username).await {
Ok(Some(_)) => {
return (
StatusCode::CONFLICT,
Json(ErrorResponse {
error: "Username already exists".to_string(),
}),
)
.into_response();
}
Ok(None) => {
}
Err(e) => {
error!("Database error during registration check: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Database error".to_string(),
}),
)
.into_response();
}
}
let existing_users = match state.store.load_users().await {
Ok(users) => users,
Err(e) => {
error!("Database error during user count check: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Database error".to_string(),
}),
)
.into_response();
}
};
let user_to_create = if existing_users.is_empty() {
if req.invite_code.is_some() {
return (
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "First user registration does not require an invitation code"
.to_string(),
}),
)
.into_response();
}
let password_hash = match PasswordService::hash_password(&req.password) {
Ok(hash) => hash,
Err(e) => {
error!("Failed to hash password: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to process password".to_string(),
}),
)
.into_response();
}
};
User::new_chairman(
req.username.clone(),
req.name.clone(),
password_hash,
req.email.clone(),
)
} else {
if req.invite_code.is_none() {
return (
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "Invitation code is required for registration".to_string(),
}),
)
.into_response();
}
let invite_code_str = req.invite_code.unwrap();
let mut invite_code = match state
.store
.load_invitation_code_by_code(&invite_code_str)
.await
{
Ok(Some(code)) => code,
Ok(None) => {
return (
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "Invalid invitation code".to_string(),
}),
)
.into_response();
}
Err(e) => {
error!("Database error during invitation code check: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Database error".to_string(),
}),
)
.into_response();
}
};
if !invite_code.is_valid() {
return (
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "Invitation code has expired or reached maximum usage".to_string(),
}),
)
.into_response();
}
let password_hash = match PasswordService::hash_password(&req.password) {
Ok(hash) => hash,
Err(e) => {
error!("Failed to hash password: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to process password".to_string(),
}),
)
.into_response();
}
};
invite_code.use_code();
if let Err(e) = state.store.update_invitation_code(&invite_code).await {
error!("Failed to update invitation code: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to update invitation code".to_string(),
}),
)
.into_response();
}
let management_users = match state.store.load_users().await {
Ok(users) => users
.into_iter()
.filter(|u| matches!(u.position, crate::domain::user::Position::Management))
.collect::<Vec<_>>(),
Err(e) => {
error!("Database error during management user count: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Database error".to_string(),
}),
)
.into_response();
}
};
User::new_management(
req.username.clone(),
req.name.clone(),
password_hash,
2 + management_users.len() as u32, req.email.clone(),
)
};
if let Err(e) = state.store.save_user(&user_to_create).await {
error!("Failed to save user: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to register user".to_string(),
}),
)
.into_response();
}
if matches!(
user_to_create.position,
crate::domain::user::Position::Chairman | crate::domain::user::Position::Management
) {
let mut org = state
.store
.load_organization()
.await
.unwrap_or_else(|_| Organization::new());
let guilty_cliff_dept_id = "guilty-cliff-line";
let guilty_cliff_dept_name = "Cliff of Contemplation Line";
let dept_exists = org.departments.iter().any(|d| d.id == guilty_cliff_dept_id);
if !dept_exists {
let dept = crate::domain::Department {
id: guilty_cliff_dept_id.to_string(),
name: guilty_cliff_dept_name.to_string(),
parent_id: None, leader_id: if matches!(
user_to_create.position,
crate::domain::user::Position::Chairman
) {
Some(user_to_create.id.clone())
} else {
None
},
};
org.add_department(dept);
}
let agent_exists = org.agents.iter().any(|a| a.id == user_to_create.id);
if !agent_exists {
let new_agent = Agent {
id: user_to_create.id.clone(),
name: user_to_create.name.clone(),
role: if matches!(
user_to_create.position,
crate::domain::user::Position::Chairman
) {
Role::simple("Cliff of Contemplation Line Supervisor".to_string(), "You are the supervisor of the Cliff of Contemplation Line, responsible for overseeing and managing senior company affairs.".to_string())
.with_responsibilities(vec!["Corporate Chairman".to_string(), "Cliff of Contemplation Line Management".to_string()])
.with_expertise(vec!["Corporate Governance".to_string(), "Strategic Planning".to_string()])
} else {
Role::simple("Cliff of Contemplation Line Member".to_string(), "You are a member of the Cliff of Contemplation Line, participating in senior management and decision-making processes.".to_string())
.with_responsibilities(vec!["Management Affairs".to_string(), "Collaborative Work".to_string()])
.with_expertise(vec!["Team Management".to_string(), "Cross-department Collaboration".to_string()])
},
department_id: Some(guilty_cliff_dept_id.to_string()),
llm_config: LLMConfig::openai("fake-api-key".to_string()),
description: None,
watched_tools: vec![],
trigger_conditions: vec![],
};
org.agents.push(new_agent);
} else {
if let Some(agent) = org.agents.iter_mut().find(|a| a.id == user_to_create.id) {
agent.department_id = Some(guilty_cliff_dept_id.to_string());
if matches!(
user_to_create.position,
crate::domain::user::Position::Chairman
) {
agent.role = Role::simple("Cliff of Contemplation Line Supervisor".to_string(), "You are the supervisor of the Cliff of Contemplation Line, responsible for overseeing and managing senior company affairs.".to_string())
.with_responsibilities(vec!["Corporate Chairman".to_string(), "Cliff of Contemplation Line Management".to_string()])
.with_expertise(vec!["Corporate Governance".to_string(), "Strategic Planning".to_string()]);
if let Some(dept) = org
.departments
.iter_mut()
.find(|d| d.id == guilty_cliff_dept_id)
{
dept.leader_id = Some(user_to_create.id.clone());
}
} else {
agent.role = Role::simple("Cliff of Contemplation Line Member".to_string(), "You are a member of the Cliff of Contemplation Line, participating in senior management and decision-making processes.".to_string())
.with_responsibilities(vec!["Management Affairs".to_string(), "Collaborative Work".to_string()])
.with_expertise(vec!["Team Management".to_string(), "Cross-department Collaboration".to_string()]);
}
}
}
if let Err(e) = state.store.save_organization(&org).await {
error!("Failed to update organization for guilty cliff line: {}", e);
}
let mut updated_user = user_to_create.clone();
updated_user.department = guilty_cliff_dept_name.to_string();
if let Err(e) = state.store.save_user(&updated_user).await {
error!(
"Failed to update user department for guilty cliff line: {}",
e
);
}
}
let final_user = if matches!(
user_to_create.position,
crate::domain::user::Position::Chairman | crate::domain::user::Position::Management
) {
let updated_user = match state
.store
.load_user_by_username(&user_to_create.username)
.await
{
Ok(Some(user)) => user,
_ => user_to_create.clone(), };
updated_user
} else {
user_to_create.clone()
};
let token: String = match state.jwt_service.generate_token(&UserInfo {
id: final_user.id.clone(),
username: final_user.username.clone(),
name: final_user.name.clone(),
email: final_user.email.clone(),
is_director: matches!(
final_user.position,
crate::domain::user::Position::Chairman | crate::domain::user::Position::Management
),
employee_id: final_user.employee_id.clone(),
position: format!("{:?}", final_user.position),
department: final_user.department.clone(),
}) {
Ok(token) => token,
Err(e) => {
error!("Failed to generate token: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to generate token".to_string(),
}),
)
.into_response();
}
};
Json(serde_json::json!({
"success": true,
"data": {
"token": token,
"user": {
"id": final_user.id,
"username": final_user.username,
"name": final_user.name,
"email": final_user.email,
"is_director": matches!(final_user.position, crate::domain::user::Position::Chairman | crate::domain::user::Position::Management),
"employee_id": final_user.employee_id,
"position": format!("{:?}", final_user.position),
"department": final_user.department,
}
}
})).into_response()
}
async fn check_username(
Query(params): Query<std::collections::HashMap<String, String>>,
) -> impl IntoResponse {
let username = params.get("username").cloned().unwrap_or_default();
let exists = username == "admin" || username == "director";
Json(serde_json::json!({
"success": true,
"data": {
"exists": exists,
"available": !exists,
}
}))
}
async fn websocket_handler(
ws: WebSocketUpgrade,
State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
ws.on_upgrade(move |socket| handle_websocket(socket, state))
}
async fn handle_websocket(mut socket: axum::extract::ws::WebSocket, state: Arc<AppState>) {
let mut rx = state.message_tx.subscribe();
info!("WebSocket connection established");
loop {
tokio::select! {
Ok(message) = rx.recv() => {
let to_str = match &message.to {
MessageTarget::Direct(id) => Some(id.clone()),
MessageTarget::Group(id) => Some(format!("group:{}", id)),
};
let msg_json = serde_json::json!({
"type": "message",
"data": {
"id": message.id,
"from": message.from,
"to": to_str,
"content": message.content,
"timestamp": message.timestamp,
}
});
if let Err(e) = socket.send(axum::extract::ws::Message::Text(
msg_json.to_string().into()
)).await {
error!("WebSocket send error: {}", e);
break;
}
}
Some(Ok(msg)) = socket.recv() => {
match msg {
axum::extract::ws::Message::Close(_) => {
info!("WebSocket connection closed");
break;
}
axum::extract::ws::Message::Ping(ping) => {
if let Err(e) = socket.send(axum::extract::ws::Message::Pong(ping)).await {
error!("WebSocket pong error: {}", e);
break;
}
}
axum::extract::ws::Message::Text(text) => {
if let Ok(client_message) = serde_json::from_str::<ClientMessage>(&text) {
match client_message {
ClientMessage::SendMessage { from, to, content } => {
if from.is_empty() || to.is_empty() || content.is_empty() {
let error_msg = serde_json::json!({
"type": "error",
"message": "Invalid message format: missing required fields"
});
if let Err(e) = socket.send(axum::extract::ws::Message::Text(
error_msg.to_string().into()
)).await {
error!("WebSocket send error: {}", e);
break;
}
continue;
}
let target = if to.starts_with("group:") {
MessageTarget::Group(to.strip_prefix("group:").unwrap_or(&to).to_string())
} else {
MessageTarget::Direct(to)
};
let message = Message {
id: uuid::Uuid::new_v4().to_string(),
from,
to: target,
content,
timestamp: Utc::now().timestamp(),
reply_to: None,
mentions: Vec::new(),
};
if let Err(e) = state.message_tx.send(message.clone()) {
error!("Failed to send message: {}", e);
}
}
ClientMessage::Ping => {
let pong_msg = serde_json::json!({
"type": "pong"
});
if let Err(e) = socket.send(axum::extract::ws::Message::Text(
pong_msg.to_string().into()
)).await {
error!("WebSocket send error: {}", e);
break;
}
}
}
} else {
let error_msg = serde_json::json!({
"type": "error",
"message": "Invalid JSON format"
});
if let Err(e) = socket.send(axum::extract::ws::Message::Text(
error_msg.to_string().into()
)).await {
error!("WebSocket send error: {}", e);
break;
}
}
}
_ => {
continue;
}
}
}
}
}
}
#[derive(Deserialize)]
pub struct CreateInviteCodeRequest {
pub max_usage: Option<u32>,
pub expires_at: Option<String>, }
async fn check_admin_permission(state: &AppState, token: &str) -> Option<UserInfo> {
match state.jwt_service.validate_token(token) {
Ok(user_info) => {
if matches!(user_info.position.as_str(), "Chairman" | "Management") {
Some(user_info)
} else {
None
}
}
Err(_) => None,
}
}
async fn get_invite_codes(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> impl IntoResponse {
let auth_header = headers
.get("authorization")
.and_then(|value| value.to_str().ok());
if let Some(auth_str) = auth_header {
if auth_str.starts_with("Bearer ") {
let token = auth_str.strip_prefix("Bearer ").unwrap();
if check_admin_permission(&state, token).await.is_some() {
match state.store.load_invitation_codes().await {
Ok(codes) => {
let codes_data: Vec<serde_json::Value> = codes
.into_iter()
.map(|code| {
serde_json::json!({
"id": code.id,
"code": code.code,
"created_by": code.created_by,
"created_at": code.created_at,
"expiry_time": code.expiry_time,
"is_used": !code.is_valid(),
"usage_count": code.current_usage,
"max_usage": code.max_usage,
})
})
.collect();
return Json(serde_json::json!({
"success": true,
"data": codes_data
}))
.into_response();
}
Err(e) => {
error!("Failed to load invitation codes: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load invitation codes".to_string(),
}),
)
.into_response();
}
}
}
}
}
(
StatusCode::FORBIDDEN,
Json(ErrorResponse {
error: "Insufficient permissions".to_string(),
}),
)
.into_response()
}
async fn create_invite_code(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Json(req): Json<CreateInviteCodeRequest>,
) -> impl IntoResponse {
let auth_header = headers
.get("authorization")
.and_then(|value| value.to_str().ok());
if let Some(auth_str) = auth_header {
if auth_str.starts_with("Bearer ") {
let token = auth_str.strip_prefix("Bearer ").unwrap();
if let Some(user_info) = check_admin_permission(&state, token).await {
let mut new_code = InvitationCode::new(user_info.id.clone(), req.max_usage);
if let Some(expires_at_str) = req.expires_at {
if let Ok(expires_at) = chrono::DateTime::parse_from_rfc3339(&expires_at_str) {
new_code.expiry_time = expires_at.timestamp();
}
}
match state.store.save_invitation_code(&new_code).await {
Ok(_) => {
return Json(serde_json::json!({
"success": true,
"data": {
"id": new_code.id,
"code": new_code.code,
"created_by": new_code.created_by,
"created_at": new_code.created_at,
"expires_at": new_code.expiry_time,
"max_usage": new_code.max_usage,
"usage_count": new_code.current_usage,
"is_active": new_code.is_valid(),
}
}))
.into_response();
}
Err(e) => {
error!("Failed to save invitation code: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to save invitation code".to_string(),
}),
)
.into_response();
}
}
}
}
}
(
StatusCode::FORBIDDEN,
Json(ErrorResponse {
error: "Insufficient permissions".to_string(),
}),
)
.into_response()
}
async fn delete_invite_code(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Path(code_id): Path<String>,
) -> impl IntoResponse {
let auth_header = headers
.get("authorization")
.and_then(|value| value.to_str().ok());
if let Some(auth_str) = auth_header {
if auth_str.starts_with("Bearer ") {
let token = auth_str.strip_prefix("Bearer ").unwrap();
if check_admin_permission(&state, token).await.is_some() {
match state.store.load_invitation_codes().await {
Ok(mut codes) => {
if let Some(pos) = codes.iter().position(|c| c.id == code_id) {
codes.remove(pos);
for code in codes {
if let Err(e) = state.store.save_invitation_code(&code).await {
error!(
"Failed to update invitation codes after deletion: {}",
e
);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to update invitation codes".to_string(),
}),
)
.into_response();
}
}
return Json(serde_json::json!({
"success": true,
"message": "Invitation code deleted successfully"
}))
.into_response();
} else {
return (
StatusCode::NOT_FOUND,
Json(ErrorResponse {
error: "Invitation code not found".to_string(),
}),
)
.into_response();
}
}
Err(e) => {
error!("Failed to load invitation codes: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load invitation codes".to_string(),
}),
)
.into_response();
}
}
}
}
}
(
StatusCode::FORBIDDEN,
Json(ErrorResponse {
error: "Insufficient permissions".to_string(),
}),
)
.into_response()
}
async fn list_chat_sessions(State(state): State<Arc<AppState>>) -> impl IntoResponse {
match state.store.load_organization().await {
Ok(org) => {
let sessions: Vec<serde_json::Value> = org
.agents
.into_iter()
.map(|agent| {
serde_json::json!({
"id": agent.id,
"name": agent.name,
"participants": [{
"id": agent.id,
"name": agent.name,
"isAgent": true,
"status": "online", "description": agent.description,
}],
"lastMessage": null,
"unreadCount": 0,
"createdAt": chrono::Utc::now().timestamp(),
"updatedAt": chrono::Utc::now().timestamp()
})
})
.collect();
Json(serde_json::json!({
"success": true,
"data": sessions
}))
.into_response()
}
Err(e) => {
error!("Failed to load organization for chat sessions: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load chat sessions".to_string(),
}),
)
.into_response()
}
}
}
async fn get_session_messages(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
) -> impl IntoResponse {
let filter = crate::core::store::MessageFilter {
from: Some(session_id.clone()), target_type: None,
to: None,
since: None,
limit: 50, };
match state.store.load_messages(filter).await {
Ok(messages) => {
let formatted_messages: Vec<serde_json::Value> = messages
.into_iter()
.map(|msg| {
let sender = if msg.from == session_id {
serde_json::json!({
"id": msg.from,
"name": get_agent_name_by_id(&state.agents, &msg.from),
"isAgent": true
})
} else {
serde_json::json!({
"id": msg.from,
"name": "Unknown User",
"isAgent": false
})
};
serde_json::json!({
"id": msg.id,
"sender": sender,
"content": msg.content,
"timestamp": msg.timestamp,
"replyTo": msg.reply_to,
"mentions": msg.mentions
})
})
.collect();
Json(serde_json::json!({
"success": true,
"data": formatted_messages
}))
.into_response()
}
Err(e) => {
error!("Failed to load messages for session {}: {}", session_id, e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load messages".to_string(),
}),
)
.into_response()
}
}
}
fn get_agent_name_by_id(agents: &[Agent], agent_id: &str) -> String {
agents
.iter()
.find(|agent| agent.id == agent_id)
.map(|agent| agent.name.clone())
.unwrap_or_else(|| "Unknown Agent".to_string())
}
async fn get_org_tree(State(state): State<Arc<AppState>>) -> impl IntoResponse {
match state.store.load_organization().await {
Ok(org) => {
let mut departments_map: std::collections::HashMap<String, serde_json::Value> =
std::collections::HashMap::new();
for dept in &org.departments {
let agent_leaders: Vec<serde_json::Value> = org
.agents
.iter()
.filter(|agent| {
agent.department_id.as_ref() == Some(&dept.id)
&& agent.role.title.contains("主管")
})
.map(|agent| {
serde_json::json!({
"id": agent.id,
"name": agent.name,
"title": agent.role.title,
"status": "online",
"description": agent.description
})
})
.collect();
let dept_agents: Vec<serde_json::Value> = org
.agents
.iter()
.filter(|agent| {
agent.department_id.as_ref() == Some(&dept.id)
&& !agent.role.title.contains("主管")
})
.map(|agent| {
serde_json::json!({
"id": agent.id,
"name": agent.name,
"title": agent.role.title,
"status": "online",
"description": agent.description
})
})
.collect();
let leader = if !agent_leaders.is_empty() {
agent_leaders.first().cloned()
} else {
None
};
let users = [&agent_leaders[..], &dept_agents[..]].concat();
departments_map.insert(
dept.id.clone(),
serde_json::json!({
"id": dept.id,
"name": dept.name,
"parentId": dept.parent_id,
"leader": leader,
"users": users,
"memberCount": users.len(),
"children": Vec::<serde_json::Value>::new() }),
);
}
let mut processed_depts = std::collections::HashMap::new();
for dept in &org.departments {
if let Some(mut dept_info) = departments_map.get(&dept.id).cloned() {
let child_depts: Vec<serde_json::Value> = org
.departments
.iter()
.filter(|child| child.parent_id.as_ref() == Some(&dept.id))
.filter_map(|child| processed_depts.get(&child.id).cloned())
.collect();
if !child_depts.is_empty() {
if let serde_json::Value::Object(ref mut obj) = dept_info {
obj.insert(
"children".to_string(),
serde_json::Value::Array(child_depts),
);
}
}
processed_depts.insert(dept.id.clone(), dept_info);
}
}
let root_depts: Vec<serde_json::Value> = org
.departments
.iter()
.filter(|dept| dept.parent_id.is_none())
.filter_map(|dept| processed_depts.get(&dept.id).cloned())
.collect();
let _agents: Vec<serde_json::Value> = org
.agents
.iter()
.map(|agent| {
serde_json::json!({
"id": agent.id,
"name": agent.name,
"title": agent.role.title,
"departmentId": agent.department_id,
"status": "online", "isOnline": true
})
})
.collect();
Json(serde_json::json!({
"success": true,
"data": root_depts }))
.into_response()
}
Err(e) => {
error!("Failed to load organization tree: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load organization tree".to_string(),
}),
)
.into_response()
}
}
}
async fn get_users(State(state): State<Arc<AppState>>, headers: HeaderMap) -> impl IntoResponse {
let auth_header = headers
.get("authorization")
.and_then(|value| value.to_str().ok());
if let Some(auth_str) = auth_header {
if auth_str.starts_with("Bearer ") {
let token = auth_str.strip_prefix("Bearer ").unwrap();
if check_admin_permission(&state, token).await.is_some() {
match state.store.load_users().await {
Ok(users) => {
let users_data: Vec<serde_json::Value> = users.into_iter().map(|user| {
serde_json::json!({
"id": user.id,
"username": user.username,
"name": user.name,
"email": user.email,
"employee_id": user.employee_id,
"position": format!("{:?}", user.position),
"department": user.department,
"created_at": user.created_at,
"is_director": matches!(user.position, crate::domain::user::Position::Chairman | crate::domain::user::Position::Management),
})
}).collect();
return Json(serde_json::json!({
"success": true,
"data": users_data
}))
.into_response();
}
Err(e) => {
error!("Failed to load users: {}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorResponse {
error: "Failed to load users".to_string(),
}),
)
.into_response();
}
}
}
}
}
(
StatusCode::FORBIDDEN,
Json(ErrorResponse {
error: "Insufficient permissions".to_string(),
}),
)
.into_response()
}
pub fn create_router(state: Arc<AppState>) -> Router {
let cors = CorsLayer::permissive();
Router::new()
.route("/api/health", get(health_check))
.route("/api/company", get(get_company))
.route("/api/agents", get(list_agents))
.route("/api/agents/{id}", get(get_agent))
.route("/api/messages", post(send_message))
.route("/api/auth/login", post(login))
.route("/api/auth/register", post(register))
.route("/api/auth/check-username", get(check_username))
.route("/api/auth/current", get(get_current_user))
.route(
"/api/admin/invite-codes",
get(get_invite_codes).post(create_invite_code),
)
.route("/api/admin/invite-codes/{id}", delete(delete_invite_code))
.route("/api/chat/list", get(list_chat_sessions))
.route("/api/chat/{session_id}/messages", get(get_session_messages))
.route("/api/org/tree", get(get_org_tree))
.route("/api/admin/users", get(get_users))
.route("/ws", get(websocket_handler))
.layer(cors)
.with_state(state)
}
pub async fn start_web_server(
bind_addr: &str,
agents: Vec<Agent>,
message_tx: broadcast::Sender<Message>,
store: Arc<dyn crate::core::store::Store>,
) -> anyhow::Result<()> {
let jwt_secret =
std::env::var("JWT_SECRET").unwrap_or_else(|_| "default_secret_key_for_dev".to_string());
let jwt_service = JwtService::new(&jwt_secret);
let state = Arc::new(AppState {
agents,
message_tx,
store,
jwt_service,
});
let app = create_router(state);
let listener = tokio::net::TcpListener::bind(bind_addr).await?;
info!("Web server started on http://{}", bind_addr);
axum::serve(listener, app).await?;
Ok(())
}