br_addon/
lib.rs

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