secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
//! 插件系统示例
//! 
//! 本示例演示如何:
//! 1. 创建插件管理器
//! 2. 加载插件(从插件目录)
//! 3. 初始化插件
//! 4. 启动插件
//! 5. 执行插件功能
//! 6. 停止插件

// 允许 unsafe 代码,用于在 configure 回调中绕过 ServiceConfig 的 Send 限制
// 这是安全的,因为我们确保在同一个线程中访问 ServiceConfig
// 注意:这是 actix-web 的限制,ServiceConfig 不是 Send 的,但我们在 configure 回调中使用它是安全的
#![allow(unsafe_code)]
#![allow(clippy::non_send_fields_in_send_ty)]
#![allow(improper_ctypes_definitions)]

use secra_plugins::{
    manager::{PluginManager, PluginManagerConfig},
};
use std::path::Path;
use std::sync::{Arc};
use actix_web::{post, App, HttpResponse, HttpServer, Responder};
use tokio::signal;
use secra_logger::{init_logger, LoggerConfig, LogOutput};
use tracing::Level;

/// 检测当前系统的目标三元组(target triple)
/// 
/// 根据当前系统的架构和操作系统,返回对应的目标三元组。
/// 支持的目标三元组:
/// - aarch64-apple-darwin (macOS Apple Silicon)
/// - x86_64-apple-darwin (macOS Intel)
/// - aarch64-unknown-linux-musl (Linux ARM64)
/// - x86_64-unknown-linux-musl (Linux x86_64)
/// - x86_64-pc-windows-gnu (Windows x86_64)
fn detect_target_triple() -> String {
    let arch = std::env::consts::ARCH;
    let os = std::env::consts::OS;
    
    match (arch, os) {
        ("aarch64", "macos") => "aarch64-apple-darwin".to_string(),
        ("x86_64", "macos") => "x86_64-apple-darwin".to_string(),
        ("aarch64", "linux") => "aarch64-unknown-linux-musl".to_string(),
        ("x86_64", "linux") => "x86_64-unknown-linux-musl".to_string(),
        ("x86_64", "windows") => "x86_64-pc-windows-gnu".to_string(),
        _ => {
            // 如果无法匹配,尝试使用编译时的目标三元组
            // 或者返回一个默认值
            if let Ok(target) = std::env::var("TARGET") {
                target
            } else {
                // 回退到基于当前架构和OS的组合
                format!("{}-unknown-{}", arch, os)
            }
        }
    }
}

// 包装类型,用于跨线程传递 ServiceConfig 指针(目前未使用)
// #[allow(improper_ctypes_definitions)]
// struct ServiceConfigPtr(*mut web::ServiceConfig);
// unsafe impl Send for ServiceConfigPtr {}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 初始化 secra-logger
    // 从环境变量 RUST_LOG 读取日志级别,如果没有设置则使用 Info 级别
    let log_level = std::env::var("RUST_LOG")
        .ok()
        .and_then(|s| {
            match s.to_lowercase().as_str() {
                "trace" => Some(Level::TRACE),
                "debug" => Some(Level::DEBUG),
                "info" => Some(Level::INFO),
                "warn" => Some(Level::WARN),
                "error" => Some(Level::ERROR),
                _ => None,
            }
        })
        .unwrap_or(Level::INFO);
    
    // 使用 secra-logger 1.0.1 版本的 API
    let config = LoggerConfig::new(
        log_level,                                    // 日志级别
        LogOutput::File,                              // 输出到文件
        "./logs/app.log",                            // 日志文件路径
        100 * 1024 * 1024,                           // 100MB 文件大小限制
        Some(7),                                     // 最多保留 7 个文件
        "secra-plugins",                             // 应用名称
    );
    
    init_logger(config)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("初始化日志失败: {}", e)))?;

    println!("=== 插件系统示例 ===");

    // 检测当前系统架构并设置 library_path
    let library_path = detect_target_triple();
    println!("检测到系统架构: {}", library_path);

    // 1. 创建插件管理器配置
    // 注意:需要配置签名验证密钥
    // 所有路径都支持相对路径,会自动转换为绝对路径
    let config = PluginManagerConfig::builder()
        .library_path(library_path)              // 根据当前系统架构设置动态库路径
        .plugin_dir("/Users/xiaowenli/lowcode/Secra/code/secra-plugins/plugins".to_string())  // 插件目录(存放 .spk 文件)- 支持相对路径
        .temp_dir("/Users/xiaowenli/lowcode/Secra/code/secra-plugins/temp".to_string())        // 临时目录(插件解包位置)- 支持相对路径
        .timeout_secs(30)                      // 超时时间(秒)
        .enable_plugin_dir_watch(true)         // 启用插件目录监听
        .ed25519_public_key("/Users/xiaowenli/lowcode/Secra/code/secra-plugins/keys/ed25519_public_key.pem".to_string())  // Ed25519 公钥路径 - 支持相对路径
        .rsa_private_key("/Users/xiaowenli/lowcode/Secra/code/secra-plugins/keys/rsa_private_key.pem".to_string())         // RSA 私钥路径 - 支持相对路径
        .build()
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("配置构建失败: {}", e)))?;

    // 2. 创建插件管理器(会自动创建 temp_dir 如果不存在)
    let manager = Arc::new(PluginManager::new(config).await
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("创建插件管理器失败: {}", e)))?);

    // 3. 确保插件目录存在(temp_dir 已在 PluginManager::new() 中自动创建)
    let plugin_dir = manager.plugin_dir.clone();
    if !Path::new(&plugin_dir).exists() {
        println!("创建插件目录: {}", plugin_dir);
        tokio::fs::create_dir_all(&plugin_dir).await?;
    }
    
    // // 检查是否有插件文件
    // let mut plugin_files = Vec::new();
    // if let Ok(mut entries) = tokio::fs::read_dir(&plugin_dir).await {
    //     while let Ok(Some(entry)) = entries.next_entry().await {
    //         if let Some(name) = entry.file_name().to_str() {
    //             if name.ends_with(".spk") {
    //                 plugin_files.push(name.to_string());
    //             }
    //         }
    //     }
    // }
    
    // if plugin_files.is_empty() {
    //     println!("\n提示: 插件目录中没有找到 .spk 文件");
    //     println!("要创建插件包,请执行以下步骤:");
    //     println!("  1. 编译插件: cd example && ./build.sh");
    //     println!("  2. 打包插件: 使用 secra-pluginctl 将动态库打包为 .spk 文件");
    //     println!("  3. 将 .spk 文件放入 {} 目录", plugin_dir);
    //     println!("\n注意: 当前代码要求配置签名验证密钥才能加载插件");
    //     println!("      如果不需要签名验证,需要修改 loader.rs 以支持可选验证");
    //     return Ok(());
    // }
    
    // println!("发现 {} 个插件文件: {:?}", plugin_files.len(), plugin_files);

    // 4. 加载所有插件
    println!("\n--- 步骤 1: 加载插件 ---");
    match manager.load_all_plugins().await {
        Ok(_) => {
            println!("插件加载完成");
            // 检查实际加载的插件数量
            match manager.get_all_plugins().await {
                Ok(plugins) => {
                    println!("实际加载的插件数量: {}", plugins.len());
                    if plugins.is_empty() {
                        println!("警告: 没有插件被成功加载,可能加载过程中出现了错误");
                        println!("请检查日志输出以获取更多信息");
                    }
                }
                Err(e) => {
                    eprintln!("查询插件失败: {}", e);
                }
            }
        }
        Err(e) => {
            eprintln!("插件加载失败: {}", e);
            // 如果插件目录为空或没有插件,这是正常的
            println!("提示: 如果这是第一次运行,请确保插件目录中有插件包");
            return Ok(());
        }
    }

    // 5. 初始化所有插件
    println!("\n--- 步骤 2: 初始化插件 ---");
    match manager.initialize_all_plugins().await {
        Ok(_) => {
            println!("插件初始化完成");
        }
        Err(e) => {
            eprintln!("插件初始化失败: {}", e);
        }
    }

    // 6. 启动所有插件
    println!("\n--- 步骤 3: 启动插件 ---");
    match manager.start_all_plugins().await {
        Ok(_) => {
            println!("插件启动完成");
        }
        Err(e) => {
            eprintln!("插件启动失败: {}", e);
        }
    }
    
    // 创建用于重启服务的 channel
    let (restart_tx, mut restart_rx) = tokio::sync::mpsc::channel::<()>(1);
    let restart_tx_clone = restart_tx.clone();

    // 文件监听任务:当事件触发时,发送重启信号
    if let Some(watch_rx) = manager.watch_rx.clone() {
        let manager_clone = manager.clone();
        tokio::task::spawn_blocking(move || {
            let handle = tokio::runtime::Handle::current();
            loop {
                match watch_rx.lock().unwrap().recv() {
                    Ok(event) => {
                        println!("event: {:?}", event);
                        println!("检测到文件变化,准备重启服务...");
                        // 在阻塞上下文中执行异步方法
                        // 1. 重新加载插件目录
                        if let Err(e) = handle.block_on(manager_clone.reload_plugin_directory()) {
                            eprintln!("重新加载插件目录失败: {}", e);
                        } else {
                            // 2. 初始化所有插件
                            if let Err(e) = handle.block_on(manager_clone.initialize_all_plugins()) {
                                eprintln!("初始化插件失败: {}", e);
                            } else {
                                // 3. 启动所有插件
                                if let Err(e) = handle.block_on(manager_clone.start_all_plugins()) {
                                    eprintln!("启动插件失败: {}", e);
                                } else {
                                    println!("插件重新加载、初始化和启动完成");
                                }
                            }
                        }
                        // 发送重启信号(忽略错误,因为可能已经关闭)
                        // 服务器重启时会通过 configure 回调重新注册路由
                        let _ = restart_tx_clone.try_send(());
                    }
                    Err(std::sync::mpsc::RecvError) => {
                        // Channel 关闭,这是正常的(比如程序退出时)
                        println!("文件监听器已关闭");
                        break;
                    }
                }
            }
        });
    }

    // 主服务循环:监听重启信号并重启服务器
    loop {
        let manager_clone = manager.clone();
        let server = HttpServer::new(move || {
            let manager = manager_clone.clone();
            App::new()
                .service(echo)
                .configure(move |cfg|{
                    if let Err(e) = manager.register_all_plugin_routes_sync(cfg) {
                        eprintln!("注册插件路由失败: {}", e);
                    }
                })
        })
        .bind(("127.0.0.1", 8080))?
        .run();
        
        let handle = server.handle();
        println!("服务已启动,监听在 http://127.0.0.1:8080");
        
        // 在后台运行服务器
        let mut server_task_handle = tokio::spawn(async move {
            server.await
        });
        
        // 等待重启信号、Ctrl+C 信号或服务器停止
        tokio::select! {
            result = &mut server_task_handle => {
                match result {
                    Ok(Ok(())) => {
                        println!("服务器正常停止");
                        return Ok(());
                    }
                    Ok(Err(e)) => {
                        eprintln!("服务器运行错误: {}", e);
                        return Err(e);
                    }
                    Err(e) => {
                        eprintln!("服务器任务错误: {}", e);
                        return Err(std::io::Error::new(std::io::ErrorKind::Other, format!("服务器任务错误: {}", e)));
                    }
                }
            }
            _ = signal::ctrl_c() => {
                println!("\n收到 Ctrl+C 信号,正在优雅地停止服务器...");
                let _ = handle.stop(true);
                // 等待服务器完全停止
                match server_task_handle.await {
                    Ok(Ok(())) | Ok(Err(_)) | Err(_) => {
                        // 服务器已停止,忽略具体结果
                    }
                }
                println!("服务器已停止");
                return Ok(());
            }
            restart_signal = restart_rx.recv() => {
                match restart_signal {
                    Some(_) => {
                        println!("收到重启信号,正在停止服务器...");
                        let _ = handle.stop(true);
                        // 等待服务器完全停止(select! 会取消 server_task_handle 分支,所以我们可以安全地等待它)
                        match server_task_handle.await {
                            Ok(Ok(())) | Ok(Err(_)) | Err(_) => {
                                // 服务器已停止,忽略具体结果
                            }
                        }
                        println!("服务器已停止,准备重启...");
                        // 短暂延迟后重启
                        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
                    }
                    None => {
                        println!("重启 channel 已关闭,退出服务循环");
                        let _ = handle.stop(true);
                        match server_task_handle.await {
                            Ok(Ok(())) | Ok(Err(_)) | Err(_) => {
                                // 服务器已停止,忽略具体结果
                            }
                        }
                        return Ok(());
                    }
                }
            }
        }
    }



}