// Charter HTTP Service - NAC L2层 Charter编译器HTTP服务 // 将 charter-compiler 包装为 HTTP API 服务 use actix_web::{web, App, HttpServer, HttpResponse, middleware}; use serde::{Deserialize, Serialize}; use std::process::Command; use std::env; use std::fs; use std::path::Path; use chrono::Utc; #[derive(Deserialize)] struct CompileRequest { /// Charter 源代码 source: String, /// 优化级别 (0-3) #[serde(default = "default_optimization")] optimization: u8, /// 是否生成调试信息 #[serde(default)] debug: bool, } fn default_optimization() -> u8 { 2 } #[derive(Serialize)] struct CompileResponse { success: bool, bytecode: Option, bytecode_size: Option, error: Option, elapsed_ms: u64, timestamp: String, } #[derive(Deserialize)] struct CheckRequest { source: String, } #[derive(Serialize)] struct CheckResponse { success: bool, valid: bool, errors: Vec, warnings: Vec, timestamp: String, } #[derive(Serialize)] struct HealthResponse { status: String, service: String, version: String, layer: String, protocol: String, timestamp: String, } /// 健康检查 async fn health() -> HttpResponse { HttpResponse::Ok().json(HealthResponse { status: "running".to_string(), service: "nac-charter-service".to_string(), version: "1.0.0".to_string(), layer: "L2".to_string(), protocol: "Charter".to_string(), timestamp: Utc::now().to_rfc3339(), }) } /// 编译 Charter 源代码 async fn compile(req: web::Json) -> HttpResponse { let start = std::time::Instant::now(); // 写入临时文件 let tmp_dir = std::env::temp_dir(); let input_path = tmp_dir.join(format!("charter_input_{}.ch", std::process::id())); let output_path = tmp_dir.join(format!("charter_output_{}.nvm", std::process::id())); if let Err(e) = fs::write(&input_path, &req.source) { return HttpResponse::InternalServerError().json(CompileResponse { success: false, bytecode: None, bytecode_size: None, error: Some(format!("写入临时文件失败: {}", e)), elapsed_ms: start.elapsed().as_millis() as u64, timestamp: Utc::now().to_rfc3339(), }); } // 调用 charter 编译器 let charter_bin = env::var("CHARTER_BIN").unwrap_or_else(|_| "/opt/nac/bin/charter".to_string()); let mut cmd = Command::new(&charter_bin); cmd.arg("compile") .arg("-i").arg(&input_path) .arg("-o").arg(&output_path) .arg("-O").arg(req.optimization.to_string()); if req.debug { cmd.arg("--debug"); } let output = match cmd.output() { Ok(o) => o, Err(e) => { let _ = fs::remove_file(&input_path); return HttpResponse::InternalServerError().json(CompileResponse { success: false, bytecode: None, bytecode_size: None, error: Some(format!("执行编译器失败: {}", e)), elapsed_ms: start.elapsed().as_millis() as u64, timestamp: Utc::now().to_rfc3339(), }); } }; let _ = fs::remove_file(&input_path); let elapsed = start.elapsed().as_millis() as u64; if output.status.success() { // 读取输出字节码 match fs::read(&output_path) { Ok(bytecode) => { let _ = fs::remove_file(&output_path); let size = bytecode.len(); let hex_bytecode = hex::encode(&bytecode); log::info!("Charter编译成功,字节码大小: {} 字节,耗时: {}ms", size, elapsed); HttpResponse::Ok().json(CompileResponse { success: true, bytecode: Some(hex_bytecode), bytecode_size: Some(size), error: None, elapsed_ms: elapsed, timestamp: Utc::now().to_rfc3339(), }) } Err(e) => { HttpResponse::InternalServerError().json(CompileResponse { success: false, bytecode: None, bytecode_size: None, error: Some(format!("读取字节码失败: {}", e)), elapsed_ms: elapsed, timestamp: Utc::now().to_rfc3339(), }) } } } else { let stderr = String::from_utf8_lossy(&output.stderr).to_string(); let stdout = String::from_utf8_lossy(&output.stdout).to_string(); let error_msg = if !stderr.is_empty() { stderr } else { stdout }; log::warn!("Charter编译失败: {}", error_msg); HttpResponse::Ok().json(CompileResponse { success: false, bytecode: None, bytecode_size: None, error: Some(error_msg), elapsed_ms: elapsed, timestamp: Utc::now().to_rfc3339(), }) } } /// 语法检查 async fn check(req: web::Json) -> HttpResponse { let tmp_dir = std::env::temp_dir(); let input_path = tmp_dir.join(format!("charter_check_{}.ch", std::process::id())); if let Err(e) = fs::write(&input_path, &req.source) { return HttpResponse::InternalServerError().json(CheckResponse { success: false, valid: false, errors: vec![format!("写入临时文件失败: {}", e)], warnings: vec![], timestamp: Utc::now().to_rfc3339(), }); } let charter_bin = env::var("CHARTER_BIN").unwrap_or_else(|_| "/opt/nac/bin/charter".to_string()); let output = Command::new(&charter_bin) .arg("check") .arg("-i").arg(&input_path) .output(); let _ = fs::remove_file(&input_path); match output { Ok(o) => { let valid = o.status.success(); let stderr = String::from_utf8_lossy(&o.stderr).to_string(); let stdout = String::from_utf8_lossy(&o.stdout).to_string(); let errors = if !valid { let msg = if !stderr.is_empty() { stderr } else { stdout }; msg.lines().map(|l| l.to_string()).filter(|l| !l.is_empty()).collect() } else { vec![] }; HttpResponse::Ok().json(CheckResponse { success: true, valid, errors, warnings: vec![], timestamp: Utc::now().to_rfc3339(), }) } Err(e) => HttpResponse::InternalServerError().json(CheckResponse { success: false, valid: false, errors: vec![format!("执行检查器失败: {}", e)], warnings: vec![], timestamp: Utc::now().to_rfc3339(), }), } } /// 版本信息 async fn version() -> HttpResponse { let charter_bin = env::var("CHARTER_BIN").unwrap_or_else(|_| "/opt/nac/bin/charter".to_string()); let output = Command::new(&charter_bin).arg("version").output(); let version_info = match output { Ok(o) => String::from_utf8_lossy(&o.stdout).trim().to_string(), Err(_) => "Charter Compiler 1.0.0 (NAC L2)".to_string(), }; HttpResponse::Ok().json(serde_json::json!({ "service": "nac-charter-service", "layer": "L2", "protocol": "Charter", "compiler_version": version_info, "timestamp": Utc::now().to_rfc3339() })) } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); let host = env::var("CHARTER_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); let port: u16 = env::var("CHARTER_PORT") .unwrap_or_else(|_| "9555".to_string()) .parse() .unwrap_or(9555); log::info!("NAC Charter HTTP Service 启动中..."); log::info!("监听地址: {}:{}", host, port); log::info!("Charter 编译器: {}", env::var("CHARTER_BIN").unwrap_or_else(|_| "/opt/nac/bin/charter".to_string())); HttpServer::new(|| { App::new() .route("/health", web::get().to(health)) .route("/compile", web::post().to(compile)) .route("/check", web::post().to(check)) .route("/version", web::get().to(version)) }) .bind(format!("{}:{}", host, port))? .run() .await }