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