259 lines
8.4 KiB
Rust
259 lines
8.4 KiB
Rust
// 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<String>,
|
||
bytecode_size: Option<usize>,
|
||
error: Option<String>,
|
||
elapsed_ms: u64,
|
||
timestamp: String,
|
||
}
|
||
|
||
#[derive(Deserialize)]
|
||
struct CheckRequest {
|
||
source: String,
|
||
}
|
||
|
||
#[derive(Serialize)]
|
||
struct CheckResponse {
|
||
success: bool,
|
||
valid: bool,
|
||
errors: Vec<String>,
|
||
warnings: Vec<String>,
|
||
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<CompileRequest>) -> 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<CheckRequest>) -> 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
|
||
}
|