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