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