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#[cfg(feature = "pgsql")]
14use log::warn;
15use crate::action::Action;
16use crate::addon::Addon;
17use crate::module::Module;
18use crate::request::Request;
19use crate::tools::{Tools, ToolsConfig};
20#[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
21use br_db::types::TableOptions;
22use json::{array, object, JsonValue};
23use lazy_static::lazy_static;
24use log::{error, info};
25use std::collections::HashMap;
26use std::path::PathBuf;
27use std::sync::Mutex;
28use std::{fs, io};
29use std::cell::RefCell;
30
31lazy_static! {
32    /// 工具集合
33    static ref PLUGIN_TOOLS: Mutex<HashMap<String,Tools>> =Mutex::new(HashMap::new());
34    static ref CONFIG: Mutex<HashMap<String,JsonValue>> =Mutex::new(HashMap::new());
35}
36
37thread_local! {
38    /// 单线程全局变量
39    static GLOBAL_DATA: RefCell<JsonValue> = RefCell::new(object!{});
40}
41/// 插件接口
42pub trait Plugin {
43    /// 加载插件
44    fn addon(name: &str) -> Result<Box<dyn Addon>, String>;
45    /// 加载模型
46    fn module(name: &str) -> Result<Box<dyn Module>, String> {
47        let (addon_name, module_name) = Self::split2(name)?;
48        Self::addon(addon_name)?.module(module_name)
49    }
50    /// 加载动作
51    fn action(name: &str) -> Result<Box<dyn Action>, String> {
52        let (addon_name, module_name, action_name) = Self::split3(name)?;
53        Self::addon(addon_name)?.module(module_name)?.action(action_name)
54    }
55    /// 内部api
56    fn api_run(name: &str, request: Request) -> Result<JsonValue, String> {
57        let (addon_name, module_name, action_name) = Self::split3(name)?;
58        match Self::addon(addon_name)?.module(module_name)?.action(action_name)?.run(request) {
59            Ok(e) => Ok(e.data),
60            Err(e) => Err(e.message),
61        }
62    }
63    /// 加载工具装置
64    /// * path 配置文件路径
65    fn load_tools(config: ToolsConfig) -> Result<(), String> {
66        if PLUGIN_TOOLS.lock().unwrap().get("tools").is_none() {
67            let res = Tools::new(config)?;
68            PLUGIN_TOOLS.lock().unwrap().insert("tools".into(), res.clone());
69        }
70        Ok(())
71    }
72    /// 获取工具
73    fn get_tools() -> Tools {
74        let tools = PLUGIN_TOOLS.lock().unwrap();
75        let tools = tools.get("tools").unwrap().clone();
76        tools
77    }
78    /// 加载全局配置文件
79    fn load_config(name: &str, config: JsonValue) {
80        CONFIG.lock().unwrap().insert(name.into(), config.clone());
81    }
82    /// 根据API清单初始化数据库
83    ///
84    /// * path 插件目录
85    #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
86    fn init_db(file_path: PathBuf) -> Result<(), String> {
87        info!("=============数据库更新开始=============");
88        let list = match fs::read_to_string(file_path.clone()) {
89            Ok(e) => json::parse(&e).unwrap_or(array![]),
90            Err(e) => return Err(format!("加载API清单失败: {e}")),
91        };
92        let mut tables = HashMap::new();
93
94        // 生成数据库文件
95        let sql = PathBuf::from("sql");
96
97        // 删除目录
98        match fs::remove_dir_all(sql.to_str().unwrap()) {
99            Ok(_) => {}
100            Err(e) => {
101                #[cfg(feature = "pgsql")]
102                warn!("目录删除失败: {e}");
103                #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql"))]
104                error!("目录删除失败: {e}");
105            }
106        }
107
108        // 创建目录
109        match fs::create_dir_all(sql.to_str().unwrap()) {
110            Ok(_) => {}
111            Err(e) => error!("目录创建失败: {e}"),
112        }
113
114        let sql_file = sql.join("sql.json");
115        let mut db_install = array![];
116
117        for api_name in list.members() {
118            let api_info = api_name.as_str().unwrap_or("").split(".").collect::<Vec<&str>>();
119            let mut addon = match Self::addon(api_info[0]) {
120                Ok(e) => e,
121                Err(_) => {
122                    continue;
123                }
124            };
125            let module = match addon.module(api_info[1]) {
126                Ok(e) => e,
127                Err(_) => {
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            if Self::get_tools().db.table_is_exist(module._table_name()) {
172                let res = Self::get_tools().db.table_update(opt);
173                match res.as_i32().unwrap() {
174                    -1 => {}
175                    0 => {
176                        info!("数据库更新情况: {} 失败", module._table_name());
177                    }
178                    1 => {
179                        info!("数据库更新情况: {} 成功", module._table_name());
180                    }
181                    _ => {}
182                }
183            } else {
184                let res = Self::get_tools().db.table_create(opt);
185                info!("安装完成情况: {} {}", module._table_name(), res);
186            }
187        }
188        info!("=============数据库更新完成=============");
189        Ok(())
190    }
191    /// 生成Swagger
192    fn swagger(
193        title: &str,
194        description: &str,
195        version: &str,
196        uaturl: &str,
197        produrl: &str,
198        tags: JsonValue,
199        paths: JsonValue,
200    ) -> JsonValue {
201        let info = object! {
202            openapi:"3.0.0",
203            info:{
204                title:title,
205                description:description,
206                version:version
207            },
208            components: {
209                securitySchemes: {
210                    BearerToken: {
211                        "type": "http",
212                        "scheme": "bearer",
213                        "bearerFormat": "Token"
214                    }
215                }
216            },
217            tags:tags,
218            security: [
219                {
220                    "BearerToken": []
221                }
222            ],
223            servers:[
224                {
225                    "url":uaturl,
226                    "description": "测试地址"
227                },
228                {
229                    "url":produrl,
230                    "description": "正式地址"
231                }
232            ],
233            paths:paths
234        };
235        info
236    }
237    /// 生成 api 列表
238    fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> io::Result<Vec<String>> {
239        #[cfg(debug_assertions)]
240        {
241            let mut plugin_list = Vec::new();
242            if path.is_dir() {
243                let res = fs::read_dir(path);
244                match res {
245                    Ok(entries) => {
246                        for entry in entries {
247                            let entry = entry.unwrap();
248                            let path = entry.path();
249                            if path.is_dir() {
250                                let res = Self::generate_api_list(
251                                    apipath.clone(),
252                                    path.to_str().unwrap().parse().unwrap(),
253                                    index + 1,
254                                )?;
255                                plugin_list.extend(res);
256                            } else if path.is_file() {
257                                if path.to_str().unwrap().ends_with("mod.rs") {
258                                    continue;
259                                }
260                                let addon = path.parent().unwrap().parent().unwrap().file_name().unwrap().to_str().unwrap();
261                                let model = path.parent().unwrap().file_name().unwrap().to_str().unwrap();
262                                let action = path.file_name().unwrap().to_str().unwrap().trim_end_matches(".rs");
263                                let api = format!("{addon}.{model}.{action}");
264                                match Self::action(api.as_str()) {
265                                    Ok(e) => plugin_list.push(e.api()),
266                                    Err(_) => continue
267                                }
268                            }
269                        }
270                    }
271                    Err(e) => return Err(io::Error::other(e.to_string()))
272                }
273            }
274            if index == 0 {
275                fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
276                fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
277                info!("=============API数量: {} 条=============", plugin_list.len());
278            }
279            Ok(plugin_list)
280        }
281        #[cfg(not(debug_assertions))]
282        {
283            let apis = fs::read_to_string(apipath).unwrap();
284            let apis = json::parse(&apis).unwrap();
285            let apis = apis.members().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<String>>();
286            info!("=============API数量: {} 条=============", apis.len());
287            Ok(apis)
288        }
289    }
290
291    /// 设置全局变量
292    fn set_global_data(key: &str, value: JsonValue) {
293        GLOBAL_DATA.with(|data| {
294            data.borrow_mut()[key] = value;
295        });
296    }
297    /// 获取全局变量数据
298    fn get_global_data() -> JsonValue {
299        GLOBAL_DATA.with(|data| {
300            data.borrow().clone()
301        })
302    }
303    /// 获取全局变量指定字段数据
304    fn get_global_data_key(key: &str) -> JsonValue {
305        GLOBAL_DATA.with(|data| {
306            data.borrow()[key].clone()
307        })
308    }
309    /// 解析 "addon.module"(零分配)
310    #[inline]
311    fn split2(name: &str) -> Result<(&str, &str), String> {
312        if let Some((a, b)) = name.split_once('.') {
313            if !a.is_empty() && !b.is_empty() {
314                Ok((a, b))
315            } else {
316                Err("模型格式不正确".to_string())
317            }
318        } else {
319            Err("模型格式不正确".to_string())
320        }
321    }
322
323    /// 解析 "addon.module.action"(零分配)
324    #[inline]
325    fn split3(name: &str) -> Result<(&str, &str, &str), String> {
326        if let Some((a, rest)) = name.split_once('.') {
327            if let Some((b, c)) = rest.split_once('.') {
328                if !a.is_empty() && !b.is_empty() && !c.is_empty() {
329                    Ok((a, b, c))
330                } else {
331                    Err("动作格式不正确".to_string())
332                }
333            } else {
334                Err("动作格式不正确".to_string())
335            }
336        } else {
337            Err("动作格式不正确".to_string())
338        }
339    }
340}
341/// API 错误响应
342#[derive(Debug, Clone)]
343pub struct ApiResponse {
344    pub types: ApiType,
345    pub code: i32,
346    pub message: String,
347    pub data: JsonValue,
348    pub success: bool,
349}
350impl ApiResponse {
351    pub fn json(self) -> JsonValue {
352        match self.types {
353            ApiType::Json => object! {
354                code: self.code,
355                message: self.message,
356                data: self.data,
357                success: self.success
358            },
359            ApiType::Redirect => self.data,
360            ApiType::Download => self.data,
361            ApiType::Preview => self.data,
362            ApiType::Txt => self.data,
363            ApiType::Html => self.data,
364        }
365    }
366    pub fn swagger(&mut self) -> JsonValue {
367        let mut content = object! {};
368        content[self.types.str().as_str()] = object! {};
369        content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
370            "array"
371        } else {
372            "object"
373        }.into();
374        content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
375
376        content[self.types.str().as_str()]["schema"]["type"] = match content[self.types.str().as_str()]["schema"]["type"].as_str().unwrap() {
377            "int" => "integer".into(),
378            _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
379        };
380        let data = object! {
381            "description":self.message.clone(),
382            "content":content
383        };
384        data
385    }
386    pub fn success(data: JsonValue, mut message: &str) -> Self {
387        if message.is_empty() {
388            message = "success";
389        }
390        Self {
391            success: true,
392            types: ApiType::Json,
393            code: 0,
394            message: message.to_string(),
395            data,
396        }
397    }
398    pub fn fail(code: i32, message: &str) -> Self {
399        Self {
400            types: ApiType::Json,
401            code,
402            message: message.to_string(),
403            data: JsonValue::Null,
404            success: false,
405        }
406    }
407    pub fn error(data: JsonValue, message: &str) -> Self {
408        Self {
409            types: ApiType::Json,
410            code: -1,
411            message: message.to_string(),
412            data,
413            success: false,
414        }
415    }
416    /// 重定向
417    pub fn redirect(url: &str) -> Self {
418        Self {
419            types: ApiType::Redirect,
420            code: 0,
421            message: "".to_string(),
422            data: url.into(),
423            success: true,
424        }
425    }
426    /// 下载
427    pub fn download(filename: &str) -> Self {
428        Self {
429            types: ApiType::Download,
430            code: 0,
431            message: "".to_string(),
432            data: filename.into(),
433            success: true,
434        }
435    }
436    /// 预览
437    pub fn preview(filename: &str) -> Self {
438        Self {
439            types: ApiType::Preview,
440            code: 0,
441            message: "".to_string(),
442            data: filename.into(),
443            success: true,
444        }
445    }
446    /// 文本
447    pub fn txt(txt: &str) -> Self {
448        Self {
449            types: ApiType::Txt,
450            code: 0,
451            message: "".to_string(),
452            data: txt.into(),
453            success: true,
454        }
455    }
456    pub fn html(data: &str) -> Self {
457        Self {
458            types: ApiType::Html,
459            code: 0,
460            message: "".to_string(),
461            data: data.into(),
462            success: true,
463        }
464    }
465}
466impl Default for ApiResponse {
467    fn default() -> Self {
468        Self {
469            types: ApiType::Json,
470            code: 0,
471            message: "".to_string(),
472            data: JsonValue::Null,
473            success: false,
474        }
475    }
476}
477/// API 响应
478#[derive(Debug, Clone)]
479pub enum ApiType {
480    /// JSON类型
481    Json,
482    /// 重定向
483    /// (重定向地址: http://xxxxxx)
484    Redirect,
485    /// 下载
486    /// (文件地址: 文件绝对地址)
487    Download,
488    /// 预览
489    /// (文件地址: 文件绝对地址)
490    Preview,
491    /// TXT格式
492    Txt,
493    /// 返回网页
494    Html,
495}
496impl ApiType {
497    pub fn str(&mut self) -> String {
498        match self {
499            ApiType::Json => "application/json",
500            ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
501            ApiType::Txt => "text/plain",
502            ApiType::Html => "text/html",
503        }.to_string()
504    }
505}