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        if PLUGIN_TOOLS.lock().unwrap().get("tools").is_none() {
79            PLUGIN_TOOLS.lock().unwrap().insert("tools".into(), Tools::new(config)?);
80        }
81        Ok(())
82    }
83    /// 获取工具
84    fn get_tools() -> Tools {
85        let tools = PLUGIN_TOOLS.lock().unwrap();
86        let tools = tools.get("tools").unwrap().clone();
87        tools
88    }
89    /// 加载全局配置文件
90    fn load_config(name: &str, config: JsonValue) {
91        CONFIG.lock().unwrap().insert(name.into(), config.clone());
92    }
93    /// 根据API清单初始化数据库
94    ///
95    /// * path 插件目录
96    #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
97    fn init_db() -> Result<(), String> {
98        info!("=============数据库更新开始=============");
99        let mut tables = HashMap::new();
100        // 生成数据库文件
101        let sql = PathBuf::from("sql");
102
103        // 删除目录
104        match fs::remove_dir_all(sql.to_str().unwrap()) {
105            Ok(_) => {}
106            Err(e) => {
107                #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
108                error!("目录删除失败: {e}");
109            }
110        }
111
112        // 创建目录
113        match fs::create_dir_all(sql.to_str().unwrap()) {
114            Ok(_) => {}
115            Err(e) => error!("目录创建失败: {e}"),
116        }
117
118        let sql_file = sql.join("sql.json");
119        let mut db_install = array![];
120
121        for api_name in GLOBAL_MODULE.wait() {
122            let module = match Self::module(api_name) {
123                Ok(e) => e,
124                Err(e) => {
125                    error!("加载模型错误: {}",e);
126                    continue;
127                }
128            };
129            if !module.table() {
130                continue;
131            }
132            if !tables.contains_key(module._table_name()) {
133                tables.insert(module._table_name(), module);
134            }
135        }
136
137        for (_, module) in tables.iter_mut() {
138            let mut opt = TableOptions::default();
139
140            let unique = module.table_unique().iter().map(|x| (*x).to_string()).collect::<Vec<String>>();
141            let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
142
143            let index = module.table_index().iter().map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>()).collect::<Vec<Vec<String>>>();
144            let index = index.iter().map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>()).collect::<Vec<Vec<&str>>>();
145
146
147            opt.set_table_name(module._table_name());
148            opt.set_table_title(module.title());
149            opt.set_table_key(module.table_key());
150            opt.set_table_fields(module.fields().clone());
151            opt.set_table_unique(unique.clone());
152            opt.set_table_index(index.clone());
153            opt.set_table_partition(module.table_partition());
154            opt.set_table_partition_columns(module.table_partition_columns());
155            // 生成sql安装文件
156            let sql = Self::get_tools().db.table(module._table_name()).fetch_sql().table_create(opt.clone());
157            let update_sql = Self::get_tools().db.table(module._table_name()).fetch_sql().table_update(opt.clone());
158            db_install.push(object! {
159                    table:module._table_name(),
160                    field:module.fields().clone(),
161                    key:module.table_key(),
162                    index:index.clone(),
163                    unique:unique.clone(),
164                    sql:sql,
165                    update_sql:update_sql
166                 }).unwrap();
167            fs::write(sql_file.clone(), db_install.to_string()).unwrap();
168
169            if Self::get_tools().db.table_is_exist(module._table_name()) {
170                let res = Self::get_tools().db.table_update(opt);
171                match res.as_i32().unwrap() {
172                    -1 => {}
173                    0 => {
174                        info!("数据库更新情况: {} 失败", module._table_name());
175                    }
176                    1 => {
177                        info!("数据库更新情况: {} 成功", module._table_name());
178                    }
179                    _ => {}
180                }
181            } else {
182                let res = Self::get_tools().db.table_create(opt);
183                info!("安装完成情况: {} {}", module._table_name(), res);
184            }
185            let init_data = module.init_data();
186            if !init_data.is_empty() {
187                let count = Self::get_tools().db.table(module._table_name()).count();
188                if count.is_empty() {
189                    let r = Self::get_tools().db.table(module._table_name()).insert_all(init_data);
190                    info!("初始化【{}】数据 {} 条",module._table_name(),r.len());
191                }
192            }
193        }
194        info!("=============数据库更新完成=============");
195        Ok(())
196    }
197
198    /// 全局插件监听入口
199    fn handles() {
200        let mut map = GLOBAL_HANDLE.lock().unwrap();
201
202        for api in GLOBAL_ADDONS.wait() {
203            let mut addon_name = match Self::addon(api.as_str()) {
204                Ok(e) => e,
205                Err(e) => {
206                    error!("插件: {api} 加载错误 {e}");
207                    continue;
208                }
209            };
210            if map.get(addon_name.name()).is_none() {
211                map.insert(addon_name.name().to_string(), addon_name.name().to_string());
212                thread::spawn(move || addon_name.handle());
213            }
214        }
215        for api in GLOBAL_MODULE.wait() {
216            let mut module_name = match Self::module(api.as_str()) {
217                Ok(e) => e,
218                Err(e) => {
219                    error!("插件: {api} 加载错误 {e}");
220                    continue;
221                }
222            };
223            if map.get(module_name.module_name()).is_none() {
224                map.insert(module_name.module_name().to_string(), module_name.module_name().to_string());
225                thread::spawn(move || module_name.handle());
226            }
227        }
228    }
229    /// 生成Swagger
230    fn swagger(
231        title: &str,
232        description: &str,
233        version: &str,
234        uaturl: &str,
235        produrl: &str,
236        tags: JsonValue,
237        paths: JsonValue,
238    ) -> JsonValue {
239        let info = object! {
240            openapi:"3.0.0",
241            info:{
242                title:title,
243                description:description,
244                version:version
245            },
246            components: {
247                securitySchemes: {
248                    BearerToken: {
249                        "type": "http",
250                        "scheme": "bearer",
251                        "bearerFormat": "Token"
252                    }
253                }
254            },
255            tags:tags,
256            security: [
257                {
258                    "BearerToken": []
259                }
260            ],
261            servers:[
262                {
263                    "url":uaturl,
264                    "description": "测试地址"
265                },
266                {
267                    "url":produrl,
268                    "description": "正式地址"
269                }
270            ],
271            paths:paths
272        };
273        info
274    }
275    /// 生成 api 列表
276    fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> Result<Vec<String>, String> {
277        #[cfg(debug_assertions)]
278        {
279            let mut plugin_list = Vec::new();
280            if path.is_dir() {
281                let res = fs::read_dir(path);
282                match res {
283                    Ok(entries) => {
284                        for entry in entries {
285                            let entry = match entry {
286                                Ok(e) => e,
287                                Err(e) => {
288                                    return Err(e.to_string())
289                                }
290                            };
291                            let path = entry.path();
292                            if path.is_dir() {
293                                let res = Self::generate_api_list(
294                                    apipath.clone(),
295                                    path.to_str().unwrap().parse().unwrap(),
296                                    index + 1,
297                                )?;
298                                plugin_list.extend(res);
299                            } else if path.is_file() {
300                                if path.to_str().unwrap().ends_with("mod.rs") {
301                                    continue;
302                                }
303                                let addon = path.parent().unwrap().parent().unwrap().file_name().unwrap().to_str().unwrap();
304                                let model = path.parent().unwrap().file_name().unwrap().to_str().unwrap();
305                                let action = path.file_name().unwrap().to_str().unwrap().trim_end_matches(".rs");
306                                let api = format!("{addon}.{model}.{action}");
307                                match Self::action(api.as_str()) {
308                                    Ok(e) => plugin_list.push(e.api()),
309                                    Err(_) => continue
310                                }
311                            }
312                        }
313                    }
314                    Err(e) => return Err(e.to_string())
315                }
316            }
317            if index == 0 {
318                fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
319                fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
320                info!("=============API数量: {} 条=============", plugin_list.len());
321                Self::_load_apis(plugin_list.clone())?;
322            }
323            Ok(plugin_list)
324        }
325        #[cfg(not(debug_assertions))]
326        {
327            let apis = fs::read_to_string(apipath).unwrap();
328            let apis = json::parse(&apis).unwrap();
329            let apis = apis.members().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<String>>();
330            info!("=============API数量: {} 条=============", apis.len());
331            Self::_load_apis(apis.clone())?;
332            Ok(apis)
333        }
334    }
335    /// 加载api清单
336    fn _load_apis(apis: Vec<String>) -> Result<(), String> {
337        let mut action_list = vec![];
338        let mut module_list = vec![];
339        let mut addons_list = vec![];
340        for api in apis {
341            let action = Self::action(api.as_str())?;
342            action_list.push(action.api());
343            if !module_list.contains(&action.module_name()) {
344                module_list.push(action.module_name());
345            }
346            if !addons_list.contains(&action.addon_name()) {
347                addons_list.push(action.addon_name());
348            }
349        }
350        let _ = GLOBAL_ACTION.set(action_list);
351        let _ = GLOBAL_MODULE.set(module_list);
352        let _ = GLOBAL_ADDONS.set(addons_list);
353        Ok(())
354    }
355    /// 设置全局变量
356    fn set_global_data(key: &str, value: JsonValue) {
357        GLOBAL_DATA.with(|data| {
358            data.borrow_mut()[key] = value;
359        });
360    }
361    /// 获取全局变量数据
362    fn get_global_data() -> JsonValue {
363        GLOBAL_DATA.with(|data| {
364            data.borrow().clone()
365        })
366    }
367    /// 获取全局变量指定字段数据
368    fn get_global_data_key(key: &str) -> JsonValue {
369        GLOBAL_DATA.with(|data| {
370            data.borrow()[key].clone()
371        })
372    }
373    /// 解析 "addon.module"(零分配)
374    #[inline]
375    fn split2(name: &str) -> Result<(&str, &str), String> {
376        let t = name.split(".").collect::<Vec<&str>>();
377        if t.len() < 2 {
378            return Err(format!("模型格式不正确: {name}"));
379        }
380        Ok((t[0], t[1]))
381    }
382
383    /// 解析 "addon.module.action"(零分配)
384    #[inline]
385    fn split3(name: &str) -> Result<(&str, &str, &str), String> {
386        if let Some((a, rest)) = name.split_once('.') {
387            if let Some((b, c)) = rest.split_once('.') {
388                if !a.is_empty() && !b.is_empty() && !c.is_empty() {
389                    Ok((a, b, c))
390                } else {
391                    Err("动作格式不正确".to_string())
392                }
393            } else {
394                Err("动作格式不正确".to_string())
395            }
396        } else {
397            Err("动作格式不正确".to_string())
398        }
399    }
400}
401/// API 错误响应
402#[derive(Debug, Clone)]
403pub struct ApiResponse {
404    pub types: ApiType,
405    pub code: i32,
406    pub message: String,
407    pub data: JsonValue,
408    pub success: bool,
409    pub timestamp: i64,
410}
411impl ApiResponse {
412    pub fn json(self) -> JsonValue {
413        match self.types {
414            ApiType::Json => object! {
415                code: self.code,
416                message: self.message,
417                data: self.data,
418                success: self.success
419            },
420            ApiType::Redirect => self.data,
421            ApiType::Download => self.data,
422            ApiType::Preview => self.data,
423            ApiType::Txt => self.data,
424            ApiType::Html => self.data,
425        }
426    }
427    pub fn swagger(&mut self) -> JsonValue {
428        let mut content = object! {};
429        content[self.types.str().as_str()] = object! {};
430        content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
431            "array"
432        } else {
433            "object"
434        }.into();
435        content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
436
437        content[self.types.str().as_str()]["schema"]["type"] = match content[self.types.str().as_str()]["schema"]["type"].as_str().unwrap() {
438            "int" => "integer".into(),
439            _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
440        };
441        let data = object! {
442            "description":self.message.clone(),
443            "content":content
444        };
445        data
446    }
447    pub fn success(data: JsonValue, mut message: &str) -> Self {
448        if message.is_empty() {
449            message = "success";
450        }
451        Self {
452            success: true,
453            types: ApiType::Json,
454            code: 0,
455            message: message.to_string(),
456            data,
457            timestamp: br_fields::datetime::Timestamp::timestamp(),
458        }
459    }
460    pub fn fail(code: i32, message: &str) -> Self {
461        Self {
462            types: ApiType::Json,
463            code,
464            message: message.to_string(),
465            data: JsonValue::Null,
466            success: false,
467            timestamp: br_fields::datetime::Timestamp::timestamp(),
468        }
469    }
470    pub fn error(data: JsonValue, message: &str) -> Self {
471        Self {
472            types: ApiType::Json,
473            code: -1,
474            message: message.to_string(),
475            data,
476            success: false,
477            timestamp: br_fields::datetime::Timestamp::timestamp(),
478        }
479    }
480    /// 重定向
481    pub fn redirect(url: &str) -> Self {
482        Self {
483            types: ApiType::Redirect,
484            code: 0,
485            message: "".to_string(),
486            data: url.into(),
487            success: true,
488            timestamp: br_fields::datetime::Timestamp::timestamp(),
489        }
490    }
491    /// 下载
492    pub fn download(filename: &str) -> Self {
493        Self {
494            types: ApiType::Download,
495            code: 0,
496            message: "".to_string(),
497            data: filename.into(),
498            success: true,
499            timestamp: br_fields::datetime::Timestamp::timestamp(),
500        }
501    }
502    /// 预览
503    pub fn preview(filename: &str) -> Self {
504        Self {
505            types: ApiType::Preview,
506            code: 0,
507            message: "".to_string(),
508            data: filename.into(),
509            success: true,
510            timestamp: br_fields::datetime::Timestamp::timestamp(),
511        }
512    }
513    /// 文本
514    pub fn txt(txt: &str) -> Self {
515        Self {
516            types: ApiType::Txt,
517            code: 0,
518            message: "".to_string(),
519            data: txt.into(),
520            success: true,
521            timestamp: br_fields::datetime::Timestamp::timestamp(),
522        }
523    }
524    pub fn html(data: &str) -> Self {
525        Self {
526            types: ApiType::Html,
527            code: 0,
528            message: "".to_string(),
529            data: data.into(),
530            success: true,
531            timestamp: br_fields::datetime::Timestamp::timestamp(),
532        }
533    }
534}
535impl Default for ApiResponse {
536    fn default() -> Self {
537        Self {
538            types: ApiType::Json,
539            code: 0,
540            message: "".to_string(),
541            data: JsonValue::Null,
542            success: false,
543            timestamp: br_fields::datetime::Timestamp::timestamp(),
544        }
545    }
546}
547/// API 响应
548#[derive(Debug, Clone)]
549pub enum ApiType {
550    /// JSON类型
551    Json,
552    /// 重定向
553    /// (重定向地址: http://xxxxxx)
554    Redirect,
555    /// 下载
556    /// (文件地址: 文件绝对地址)
557    Download,
558    /// 预览
559    /// (文件地址: 文件绝对地址)
560    Preview,
561    /// TXT格式
562    Txt,
563    /// 返回网页
564    Html,
565}
566impl ApiType {
567    pub fn str(&mut self) -> String {
568        match self {
569            ApiType::Json => "application/json",
570            ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
571            ApiType::Txt => "text/plain",
572            ApiType::Html => "text/html",
573        }.to_string()
574    }
575}