br_addon/
lib.rs

1/// 功能
2pub mod action;
3/// 插件
4pub mod addon;
5/// 模块
6pub mod module;
7/// 请求数据
8pub mod request;
9/// api清单
10pub mod swagger;
11/// 工具
12pub mod tools;
13#[allow(clippy::too_many_arguments)]
14#[cfg(any(feature = "sqlite", feature = "mssql", feature = "mysql", feature = "pgsql"))]
15/// 数据表构造
16pub mod tables;
17
18use crate::action::Action;
19use crate::addon::Addon;
20use crate::module::Module;
21use crate::request::Request;
22use crate::tools::{Tools, ToolsConfig};
23#[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
24use br_db::types::TableOptions;
25use json::{array, object, JsonValue};
26use lazy_static::lazy_static;
27use log::{error, info};
28use std::collections::HashMap;
29use std::path::PathBuf;
30use std::sync::{Mutex, OnceLock};
31use std::{fs, thread};
32use std::cell::RefCell;
33use std::sync::LazyLock;
34
35lazy_static! {
36    /// 工具集合
37    static ref PLUGIN_TOOLS: Mutex<HashMap<String,Tools>> =Mutex::new(HashMap::new());
38    static ref CONFIG: Mutex<HashMap<String,JsonValue>> =Mutex::new(HashMap::new());
39}
40/// 全局监听线程
41static GLOBAL_HANDLE: LazyLock<Mutex<HashMap<String, String>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
42/// 全局加载的插件清单
43pub static GLOBAL_ADDONS: OnceLock<Vec<String>> = OnceLock::new();
44/// 全局加载的模块清单
45pub static GLOBAL_MODULE: OnceLock<Vec<String>> = OnceLock::new();
46/// 全局加载的动作清单
47pub static GLOBAL_ACTION: OnceLock<Vec<String>> = OnceLock::new();
48
49thread_local! {
50    /// 单线程全局变量
51    static GLOBAL_DATA: RefCell<JsonValue> = RefCell::new(object!{});
52}
53/// 插件接口
54pub trait Plugin {
55    /// 加载插件
56    fn addon(name: &str) -> Result<Box<dyn Addon>, String>;
57    /// 加载模型
58    fn module(name: &str) -> Result<Box<dyn Module>, String> {
59        let (addon_name, module_name) = Self::split2(name)?;
60        Self::addon(addon_name)?.module(module_name)
61    }
62    /// 加载动作
63    fn action(name: &str) -> Result<Box<dyn Action>, String> {
64        let (addon_name, module_name, action_name) = Self::split3(name)?;
65        Self::addon(addon_name)?.module(module_name)?.action(action_name)
66    }
67    /// 内部api
68    fn api_run(name: &str, request: Request) -> Result<JsonValue, String> {
69        let (addon_name, module_name, action_name) = Self::split3(name)?;
70        match Self::addon(addon_name)?.module(module_name)?.action(action_name)?.run(request) {
71            Ok(e) => Ok(e.data),
72            Err(e) => Err(e.message),
73        }
74    }
75    /// 加载工具装置
76    /// * path 配置文件路径
77    fn load_tools(config: ToolsConfig) -> Result<(), String> {
78        let mut tools_map = PLUGIN_TOOLS.lock().unwrap_or_else(|e| e.into_inner());
79        if tools_map.get("tools").is_none() {
80            tools_map.insert("tools".into(), Tools::new(config)?);
81        }
82        Ok(())
83    }
84    /// 获取工具
85    fn get_tools() -> Tools {
86        let tools_map = PLUGIN_TOOLS.lock().unwrap_or_else(|e| e.into_inner());
87        let tools = tools_map.get("tools").unwrap().clone();
88        tools
89    }
90    /// 加载全局配置文件
91    fn load_config(name: &str, config: JsonValue) {
92        let mut config_map = CONFIG.lock().unwrap_or_else(|e| e.into_inner());
93        config_map.insert(name.into(), config.clone());
94    }
95    /// 根据API清单初始化数据库
96    ///
97    /// * path 插件目录
98    #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
99    fn init_db() -> Result<(), String> {
100        info!("=============数据库更新开始=============");
101        let mut tables = HashMap::new();
102        // 生成数据库文件
103        let sql = PathBuf::from("sql");
104
105        // 删除目录
106        match fs::remove_dir_all(sql.to_str().unwrap()) {
107            Ok(_) => {}
108            Err(e) => {
109                #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
110                error!("目录删除失败: {e}");
111            }
112        }
113
114        // 创建目录
115        match fs::create_dir_all(sql.to_str().unwrap()) {
116            Ok(_) => {}
117            Err(e) => error!("目录创建失败: {e}"),
118        }
119
120        let sql_file = sql.join("sql.json");
121        let mut db_install = array![];
122
123        for api_name in GLOBAL_MODULE.wait() {
124            let module = match Self::module(api_name) {
125                Ok(e) => e,
126                Err(e) => {
127                    error!("加载模型错误: {}",e);
128                    continue;
129                }
130            };
131            if !module.table() {
132                continue;
133            }
134            if !tables.contains_key(module._table_name()) {
135                tables.insert(module._table_name(), module);
136            }
137        }
138
139        for (_, module) in tables.iter_mut() {
140            let mut opt = TableOptions::default();
141
142            let unique = module.table_unique().iter().map(|x| (*x).to_string()).collect::<Vec<String>>();
143            let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
144
145            let index = module.table_index().iter().map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>()).collect::<Vec<Vec<String>>>();
146            let index = index.iter().map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>()).collect::<Vec<Vec<&str>>>();
147
148
149            opt.set_table_name(module._table_name());
150            opt.set_table_title(module.title());
151            opt.set_table_key(module.table_key());
152            opt.set_table_fields(module.fields().clone());
153            opt.set_table_unique(unique.clone());
154            opt.set_table_index(index.clone());
155            opt.set_table_partition(module.table_partition());
156            opt.set_table_partition_columns(module.table_partition_columns());
157            // 生成sql安装文件
158            let sql = Self::get_tools().db.table(module._table_name()).fetch_sql().table_create(opt.clone());
159            let update_sql = Self::get_tools().db.table(module._table_name()).fetch_sql().table_update(opt.clone());
160            db_install.push(object! {
161                    table:module._table_name(),
162                    field:module.fields().clone(),
163                    key:module.table_key(),
164                    index:index.clone(),
165                    unique:unique.clone(),
166                    sql:sql,
167                    update_sql:update_sql
168                 }).unwrap();
169            fs::write(sql_file.clone(), db_install.to_string()).unwrap();
170
171            // #region agent log
172            {
173                use std::fs::OpenOptions;
174                use std::io::Write;
175                if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
176                    let _ = writeln!(file, r#"{{"id":"log_table_check","timestamp":{},"location":"br-addon/src/lib.rs:171","message":"检查表是否存在","data":{{"table_name":"{}","hypothesisId":"A"}},"sessionId":"debug-session","runId":"run1"}}"#, 
177                        std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
178                        module._table_name());
179                }
180            }
181            // #endregion
182            let table_exists = Self::get_tools().db.table_is_exist(module._table_name());
183            // #region agent log
184            {
185                use std::fs::OpenOptions;
186                use std::io::Write;
187                if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
188                    let _ = writeln!(file, r#"{{"id":"log_table_exists_result","timestamp":{},"location":"br-addon/src/lib.rs:172","message":"表存在性检查结果","data":{{"table_name":"{}","exists":{},"hypothesisId":"A"}},"sessionId":"debug-session","runId":"run1"}}"#, 
189                        std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
190                        module._table_name(), table_exists);
191                }
192            }
193            // #endregion
194            if table_exists {
195                // #region agent log
196                {
197                    use std::fs::OpenOptions;
198                    use std::io::Write;
199                    if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
200                        let _ = writeln!(file, r#"{{"id":"log_table_update_start","timestamp":{},"location":"br-addon/src/lib.rs:173","message":"开始更新表","data":{{"table_name":"{}","hypothesisId":"B"}},"sessionId":"debug-session","runId":"run1"}}"#, 
201                            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
202                            module._table_name());
203                    }
204                }
205                // #endregion
206                let res = Self::get_tools().db.table_update(opt);
207                // #region agent log
208                {
209                    use std::fs::OpenOptions;
210                    use std::io::Write;
211                    if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
212                        let _ = writeln!(file, r#"{{"id":"log_table_update_result","timestamp":{},"location":"br-addon/src/lib.rs:174","message":"表更新结果","data":{{"table_name":"{}","result":{},"hypothesisId":"B"}},"sessionId":"debug-session","runId":"run1"}}"#, 
213                            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
214                            module._table_name(), res);
215                    }
216                }
217                // #endregion
218                // 安全地处理返回值:可能是整数(-1, 0, 1)或布尔值(true/false)
219                let result = res.as_i32()
220                    .or_else(|| {
221                        // 如果不是整数,尝试作为布尔值处理
222                        if res.is_boolean() {
223                            Some(if res.as_bool().unwrap_or(false) { 1 } else { 0 })
224                        } else {
225                            None
226                        }
227                    })
228                    .unwrap_or(0); // 如果都无法解析,默认为失败
229                
230                match result {
231                    -1 => {}
232                    0 => {
233                        info!("数据库更新情况: {} 失败", module._table_name());
234                    }
235                    1 => {
236                        info!("数据库更新情况: {} 成功", module._table_name());
237                    }
238                    _ => {}
239                }
240            } else {
241                // #region agent log
242                {
243                    use std::fs::OpenOptions;
244                    use std::io::Write;
245                    if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
246                        let _ = writeln!(file, r#"{{"id":"log_table_create_start","timestamp":{},"location":"br-addon/src/lib.rs:183","message":"开始创建表","data":{{"table_name":"{}","hypothesisId":"C"}},"sessionId":"debug-session","runId":"run1"}}"#, 
247                            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
248                            module._table_name());
249                    }
250                }
251                // #endregion
252                let res = Self::get_tools().db.table_create(opt);
253                // #region agent log
254                {
255                    use std::fs::OpenOptions;
256                    use std::io::Write;
257                    if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/Users/xiaduan/coding/scs/.cursor/debug.log") {
258                        let _ = writeln!(file, r#"{{"id":"log_table_create_result","timestamp":{},"location":"br-addon/src/lib.rs:184","message":"表创建结果","data":{{"table_name":"{}","result":{},"hypothesisId":"C"}},"sessionId":"debug-session","runId":"run1"}}"#, 
259                            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis(),
260                            module._table_name(), res);
261                    }
262                }
263                // #endregion
264                info!("安装完成情况: {} {}", module._table_name(), res);
265            }
266            let init_data = module.init_data();
267            if !init_data.is_empty() {
268                let count = Self::get_tools().db.table(module._table_name()).count();
269                if count.is_empty() {
270                    let r = Self::get_tools().db.table(module._table_name()).insert_all(init_data);
271                    info!("初始化【{}】数据 {} 条",module._table_name(),r.len());
272                }
273            }
274        }
275        info!("=============数据库更新完成=============");
276        Ok(())
277    }
278
279    /// 全局插件监听入口
280    fn handles() {
281        let mut map = GLOBAL_HANDLE.lock().unwrap_or_else(|e| e.into_inner());
282
283        for api in GLOBAL_ADDONS.wait() {
284            let mut addon_name = match Self::addon(api.as_str()) {
285                Ok(e) => e,
286                Err(e) => {
287                    error!("插件: {api} 加载错误 {e}");
288                    continue;
289                }
290            };
291            if map.get(addon_name.name()).is_none() {
292                map.insert(addon_name.name().to_string(), addon_name.name().to_string());
293                thread::spawn(move || addon_name.handle());
294            }
295        }
296        for api in GLOBAL_MODULE.wait() {
297            let mut module_name = match Self::module(api.as_str()) {
298                Ok(e) => e,
299                Err(e) => {
300                    error!("插件: {api} 加载错误 {e}");
301                    continue;
302                }
303            };
304            if map.get(module_name.module_name()).is_none() {
305                map.insert(module_name.module_name().to_string(), module_name.module_name().to_string());
306                thread::spawn(move || module_name.handle());
307            }
308        }
309    }
310    /// 生成Swagger
311    fn swagger(
312        title: &str,
313        description: &str,
314        version: &str,
315        uaturl: &str,
316        produrl: &str,
317        tags: JsonValue,
318        paths: JsonValue,
319    ) -> JsonValue {
320        let info = object! {
321            openapi:"3.0.0",
322            info:{
323                title:title,
324                description:description,
325                version:version
326            },
327            components: {
328                securitySchemes: {
329                    BearerToken: {
330                        "type": "http",
331                        "scheme": "bearer",
332                        "bearerFormat": "Token"
333                    }
334                }
335            },
336            tags:tags,
337            security: [
338                {
339                    "BearerToken": []
340                }
341            ],
342            servers:[
343                {
344                    "url":uaturl,
345                    "description": "测试地址"
346                },
347                {
348                    "url":produrl,
349                    "description": "正式地址"
350                }
351            ],
352            paths:paths
353        };
354        info
355    }
356    /// 生成 api 列表
357    fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> Result<Vec<String>, String> {
358        #[cfg(debug_assertions)]
359        {
360            let mut plugin_list = Vec::new();
361            if path.is_dir() {
362                let res = fs::read_dir(path);
363                match res {
364                    Ok(entries) => {
365                        for entry in entries {
366                            let entry = match entry {
367                                Ok(e) => e,
368                                Err(e) => {
369                                    return Err(e.to_string())
370                                }
371                            };
372                            let path = entry.path();
373                            if path.is_dir() {
374                                let res = Self::generate_api_list(
375                                    apipath.clone(),
376                                    path.to_str().unwrap().parse().unwrap(),
377                                    index + 1,
378                                )?;
379                                plugin_list.extend(res);
380                            } else if path.is_file() {
381                                if path.to_str().unwrap().ends_with("mod.rs") {
382                                    continue;
383                                }
384                                let addon = path.parent().unwrap().parent().unwrap().file_name().unwrap().to_str().unwrap();
385                                let model = path.parent().unwrap().file_name().unwrap().to_str().unwrap();
386                                let action = path.file_name().unwrap().to_str().unwrap().trim_end_matches(".rs");
387                                let api = format!("{addon}.{model}.{action}");
388                                match Self::action(api.as_str()) {
389                                    Ok(e) => plugin_list.push(e.api()),
390                                    Err(_) => continue
391                                }
392                            }
393                        }
394                    }
395                    Err(e) => return Err(e.to_string())
396                }
397            }
398            if index == 0 {
399                fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
400                fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
401                info!("=============API数量: {} 条=============", plugin_list.len());
402                Self::_load_apis(plugin_list.clone())?;
403            }
404            Ok(plugin_list)
405        }
406        #[cfg(not(debug_assertions))]
407        {
408            let apis = fs::read_to_string(apipath).unwrap();
409            let apis = json::parse(&apis).unwrap();
410            let apis = apis.members().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<String>>();
411            info!("=============API数量: {} 条=============", apis.len());
412            Self::_load_apis(apis.clone())?;
413            Ok(apis)
414        }
415    }
416    /// 加载api清单
417    fn _load_apis(apis: Vec<String>) -> Result<(), String> {
418        let mut action_list = vec![];
419        let mut module_list = vec![];
420        let mut addons_list = vec![];
421        for api in apis {
422            let action = Self::action(api.as_str())?;
423            action_list.push(action.api());
424            if !module_list.contains(&action.module_name()) {
425                module_list.push(action.module_name());
426            }
427            if !addons_list.contains(&action.addon_name()) {
428                addons_list.push(action.addon_name());
429            }
430        }
431        let _ = GLOBAL_ACTION.set(action_list);
432        let _ = GLOBAL_MODULE.set(module_list);
433        let _ = GLOBAL_ADDONS.set(addons_list);
434        Ok(())
435    }
436    /// 设置全局变量
437    fn set_global_data(key: &str, value: JsonValue) {
438        GLOBAL_DATA.with(|data| {
439            data.borrow_mut()[key] = value;
440        });
441    }
442    /// 获取全局变量数据
443    fn get_global_data() -> JsonValue {
444        GLOBAL_DATA.with(|data| {
445            data.borrow().clone()
446        })
447    }
448    /// 获取全局变量指定字段数据
449    fn get_global_data_key(key: &str) -> JsonValue {
450        GLOBAL_DATA.with(|data| {
451            data.borrow()[key].clone()
452        })
453    }
454    /// 解析 "addon.module"(零分配)
455    #[inline]
456    fn split2(name: &str) -> Result<(&str, &str), String> {
457        let t = name.split(".").collect::<Vec<&str>>();
458        if t.len() < 2 {
459            return Err(format!("模型格式不正确: {name}"));
460        }
461        Ok((t[0], t[1]))
462    }
463
464    /// 解析 "addon.module.action"(零分配)
465    #[inline]
466    fn split3(name: &str) -> Result<(&str, &str, &str), String> {
467        if let Some((a, rest)) = name.split_once('.') {
468            if let Some((b, c)) = rest.split_once('.') {
469                if !a.is_empty() && !b.is_empty() && !c.is_empty() {
470                    Ok((a, b, c))
471                } else {
472                    Err("动作格式不正确".to_string())
473                }
474            } else {
475                Err("动作格式不正确".to_string())
476            }
477        } else {
478            Err("动作格式不正确".to_string())
479        }
480    }
481}
482/// API 错误响应
483#[derive(Debug, Clone)]
484pub struct ApiResponse {
485    pub types: ApiType,
486    pub code: i32,
487    pub message: String,
488    pub data: JsonValue,
489    pub success: bool,
490    pub timestamp: i64,
491}
492impl ApiResponse {
493    pub fn json(self) -> JsonValue {
494        match self.types {
495            ApiType::Json => object! {
496                code: self.code,
497                message: self.message,
498                data: self.data,
499                success: self.success
500            },
501            ApiType::Redirect => self.data,
502            ApiType::Download => self.data,
503            ApiType::Preview => self.data,
504            ApiType::Txt => self.data,
505            ApiType::Html => self.data,
506        }
507    }
508    pub fn swagger(&mut self) -> JsonValue {
509        let mut content = object! {};
510        content[self.types.str().as_str()] = object! {};
511        content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
512            "array"
513        } else {
514            "object"
515        }.into();
516        content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
517
518        content[self.types.str().as_str()]["schema"]["type"] = match content[self.types.str().as_str()]["schema"]["type"].as_str().unwrap() {
519            "int" => "integer".into(),
520            _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
521        };
522        let data = object! {
523            "description":self.message.clone(),
524            "content":content
525        };
526        data
527    }
528    pub fn success(data: JsonValue, mut message: &str) -> Self {
529        if message.is_empty() {
530            message = "success";
531        }
532        Self {
533            success: true,
534            types: ApiType::Json,
535            code: 0,
536            message: message.to_string(),
537            data,
538            timestamp: br_fields::datetime::Timestamp::timestamp(),
539        }
540    }
541    pub fn fail(code: i32, message: &str) -> Self {
542        Self {
543            types: ApiType::Json,
544            code,
545            message: message.to_string(),
546            data: JsonValue::Null,
547            success: false,
548            timestamp: br_fields::datetime::Timestamp::timestamp(),
549        }
550    }
551    pub fn error(data: JsonValue, message: &str) -> Self {
552        Self {
553            types: ApiType::Json,
554            code: -1,
555            message: message.to_string(),
556            data,
557            success: false,
558            timestamp: br_fields::datetime::Timestamp::timestamp(),
559        }
560    }
561    /// 重定向
562    pub fn redirect(url: &str) -> Self {
563        Self {
564            types: ApiType::Redirect,
565            code: 0,
566            message: "".to_string(),
567            data: url.into(),
568            success: true,
569            timestamp: br_fields::datetime::Timestamp::timestamp(),
570        }
571    }
572    /// 下载
573    pub fn download(filename: &str) -> Self {
574        Self {
575            types: ApiType::Download,
576            code: 0,
577            message: "".to_string(),
578            data: filename.into(),
579            success: true,
580            timestamp: br_fields::datetime::Timestamp::timestamp(),
581        }
582    }
583    /// 预览
584    pub fn preview(filename: &str) -> Self {
585        Self {
586            types: ApiType::Preview,
587            code: 0,
588            message: "".to_string(),
589            data: filename.into(),
590            success: true,
591            timestamp: br_fields::datetime::Timestamp::timestamp(),
592        }
593    }
594    /// 文本
595    pub fn txt(txt: &str) -> Self {
596        Self {
597            types: ApiType::Txt,
598            code: 0,
599            message: "".to_string(),
600            data: txt.into(),
601            success: true,
602            timestamp: br_fields::datetime::Timestamp::timestamp(),
603        }
604    }
605    pub fn html(data: &str) -> Self {
606        Self {
607            types: ApiType::Html,
608            code: 0,
609            message: "".to_string(),
610            data: data.into(),
611            success: true,
612            timestamp: br_fields::datetime::Timestamp::timestamp(),
613        }
614    }
615}
616impl Default for ApiResponse {
617    fn default() -> Self {
618        Self {
619            types: ApiType::Json,
620            code: 0,
621            message: "".to_string(),
622            data: JsonValue::Null,
623            success: false,
624            timestamp: br_fields::datetime::Timestamp::timestamp(),
625        }
626    }
627}
628/// API 响应
629#[derive(Debug, Clone)]
630pub enum ApiType {
631    /// JSON类型
632    Json,
633    /// 重定向
634    /// (重定向地址: http://xxxxxx)
635    Redirect,
636    /// 下载
637    /// (文件地址: 文件绝对地址)
638    Download,
639    /// 预览
640    /// (文件地址: 文件绝对地址)
641    Preview,
642    /// TXT格式
643    Txt,
644    /// 返回网页
645    Html,
646}
647impl ApiType {
648    pub fn str(&mut self) -> String {
649        match self {
650            ApiType::Json => "application/json",
651            ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
652            ApiType::Txt => "text/plain",
653            ApiType::Html => "text/html",
654        }.to_string()
655    }
656}