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])?
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            let api_info = api_name
109                .as_str()
110                .unwrap_or("")
111                .split(".")
112                .collect::<Vec<&str>>();
113            let mut addon = match Self::addon(api_info[0]) {
114                Ok(e) => e,
115                Err(_) => {
116                    continue;
117                }
118            };
119            let mut module = match addon.module(api_info[1]) {
120                Ok(e) => e,
121                Err(_) => {
122                    continue;
123                }
124            };
125            if !module.table() {
126                continue;
127            }
128            api_list.push(api_name.to_string().clone());
129
130            let mut opt = TableOptions::default();
131
132            let unique = module
133                .table_unique()
134                .iter()
135                .map(|x| x.to_string())
136                .collect::<Vec<String>>();
137            let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
138
139            let index = module
140                .table_index()
141                .iter()
142                .map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>())
143                .collect::<Vec<Vec<String>>>();
144            let index = index
145                .iter()
146                .map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>())
147                .collect::<Vec<Vec<&str>>>();
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);
154            opt.set_table_index(index);
155            opt.set_table_partition(module.table_partition());
156            opt.set_table_partition_columns(module.table_partition_columns());
157
158            if table_name_list.contains(&module._table_name()) {
159                continue;
160            }
161            table_name_list.push(module._table_name());
162            if Self::get_tools().db.table_is_exist(module._table_name()) {
163                let res = Self::get_tools().db.table_update_new(opt);
164                match res.as_i32().unwrap() {
165                    -1 => {}
166                    0 => {
167                        info!("数据库更新情况: {} 失败", module._table_name());
168                    }
169                    1 => {
170                        info!("数据库更新情况: {} 成功", module._table_name());
171                    }
172                    _ => {}
173                }
174            } else {
175                let res = Self::get_tools().db.table_create_new(opt);
176                info!("安装完成情况: {} {}", module._table_name(), res);
177            }
178        }
179        info!("=============数据库更新完成=============");
180        info!("=============API数量: {} 条=============", api_list.len());
181        fs::write(file_path, JsonValue::from(api_list.clone()).to_string()).unwrap();
182        Ok(api_list)
183    }
184    /// 生成Swagger
185    fn swagger(
186        title: &str,
187        description: &str,
188        version: &str,
189        uaturl: &str,
190        produrl: &str,
191        tags: JsonValue,
192        paths: JsonValue,
193    ) -> JsonValue {
194        let info = object! {
195            openapi:"3.0.0",
196            info:{
197                title:title,
198                description:description,
199                version:version
200            },
201            components: {
202                securitySchemes: {
203                    BearerToken: {
204                        "type": "http",
205                        "scheme": "bearer",
206                        "bearerFormat": "Token"
207                    }
208                }
209            },
210            tags:tags,
211            security: [
212                {
213                    "BearerToken": []
214                }
215            ],
216            servers:[
217                {
218                    "url":uaturl,
219                    "description": "测试地址"
220                },
221                {
222                    "url":produrl,
223                    "description": "正式地址"
224                }
225            ],
226            paths:paths
227        };
228        info
229    }
230    /// 生成 api 列表
231    fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> io::Result<Vec<String>> {
232        #[cfg(debug_assertions)]
233        {
234            let mut plugin_list = vec![];
235            if path.is_dir() {
236                let res = fs::read_dir(path);
237                match res {
238                    Ok(entries) => {
239                        for entry in entries {
240                            let entry = entry.unwrap();
241                            let path = entry.path();
242                            if path.is_dir() {
243                                let res = Self::generate_api_list(
244                                    apipath.clone(),
245                                    path.to_str().unwrap().parse().unwrap(),
246                                    index + 1,
247                                )?;
248                                plugin_list.extend(res);
249                            } else if path.is_file() {
250                                if path.to_str().unwrap().ends_with("mod.rs") {
251                                    continue;
252                                }
253                                let addon = path
254                                    .parent()
255                                    .unwrap()
256                                    .parent()
257                                    .unwrap()
258                                    .file_name()
259                                    .unwrap()
260                                    .to_str()
261                                    .unwrap();
262                                let model = path
263                                    .parent()
264                                    .unwrap()
265                                    .file_name()
266                                    .unwrap()
267                                    .to_str()
268                                    .unwrap();
269                                let action = path
270                                    .file_name()
271                                    .unwrap()
272                                    .to_str()
273                                    .unwrap()
274                                    .trim_end_matches(".rs");
275                                plugin_list.push(format!("{}.{}.{}", addon, model, action));
276                            }
277                        }
278                    }
279                    Err(e) => {
280                        return Err(std::io::Error::new(
281                            std::io::ErrorKind::Other,
282                            e.to_string(),
283                        ))
284                    }
285                }
286            }
287            if index == 0 {
288                fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
289                fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
290            }
291            Ok(plugin_list)
292        }
293        #[cfg(not(debug_assertions))]
294        {
295            let apis = fs::read_to_string(apipath).unwrap();
296            let apis = json::parse(&apis).unwrap();
297            let mut apis = apis
298                .members()
299                .map(|x| x.as_str().unwrap().to_string())
300                .collect::<Vec<String>>();
301            Ok(apis)
302        }
303    }
304}
305/// API 错误响应
306#[derive(Debug, Clone)]
307pub struct ApiResponse {
308    pub types: ApiType,
309    pub code: i32,
310    pub message: String,
311    pub data: JsonValue,
312    pub success: bool,
313}
314impl ApiResponse {
315    pub fn json(self) -> JsonValue {
316        match self.types {
317            ApiType::Json => object! {
318                code: self.code,
319                message: self.message,
320                data: self.data,
321                success: self.success
322            },
323            ApiType::Redirect => self.data,
324            ApiType::Download => self.data,
325            ApiType::Preview => self.data,
326            ApiType::Txt => self.data,
327        }
328    }
329    pub fn swagger(&mut self) -> JsonValue {
330        let mut content = object! {};
331        content[self.types.str().as_str()] = object! {};
332        content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
333            "array"
334        } else {
335            "object"
336        }
337        .into();
338        content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
339
340        content[self.types.str().as_str()]["schema"]["type"] = match content
341            [self.types.str().as_str()]["schema"]["type"]
342            .as_str()
343            .unwrap()
344        {
345            "int" => "integer".into(),
346            _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
347        };
348        let data = object! {
349            "description":self.message.clone(),
350            "content":content
351        };
352        data
353    }
354    pub fn success(data: JsonValue, mut message: &str) -> Self {
355        if message.is_empty() {
356            message = "success";
357        }
358        Self {
359            success: true,
360            types: ApiType::Json,
361            code: 0,
362            message: message.to_string(),
363            data,
364        }
365    }
366    pub fn fail(code: i32, message: &str) -> Self {
367        Self {
368            types: ApiType::Json,
369            code,
370            message: message.to_string(),
371            data: JsonValue::Null,
372            success: false,
373        }
374    }
375    pub fn error(data: JsonValue, message: &str) -> Self {
376        Self {
377            types: ApiType::Json,
378            code: -1,
379            message: message.to_string(),
380            data,
381            success: false,
382        }
383    }
384    /// 重定向
385    pub fn redirect(url: &str) -> Self {
386        Self {
387            types: ApiType::Redirect,
388            code: 0,
389            message: "".to_string(),
390            data: url.into(),
391            success: true,
392        }
393    }
394    /// 下载
395    pub fn download(filename: &str) -> Self {
396        Self {
397            types: ApiType::Download,
398            code: 0,
399            message: "".to_string(),
400            data: filename.into(),
401            success: true,
402        }
403    }
404    /// 预览
405    pub fn preview(filename: &str) -> Self {
406        Self {
407            types: ApiType::Preview,
408            code: 0,
409            message: "".to_string(),
410            data: filename.into(),
411            success: true,
412        }
413    }
414    /// 文本
415    pub fn txt(txt: &str) -> Self {
416        Self {
417            types: ApiType::Txt,
418            code: 0,
419            message: "".to_string(),
420            data: txt.into(),
421            success: true,
422        }
423    }
424}
425impl Default for ApiResponse {
426    fn default() -> Self {
427        Self {
428            types: ApiType::Json,
429            code: 0,
430            message: "".to_string(),
431            data: JsonValue::Null,
432            success: false,
433        }
434    }
435}
436/// API 响应
437#[derive(Debug, Clone)]
438pub enum ApiType {
439    /// JSON类型
440    Json,
441    /// 重定向
442    /// (重定向地址: http://xxxxxx)
443    Redirect,
444    /// 下载
445    /// (文件地址: 文件绝对地址)
446    Download,
447    /// 预览
448    /// (文件地址: 文件绝对地址)
449    Preview,
450    /// TXT格式
451    Txt,
452}
453impl ApiType {
454    pub fn str(&mut self) -> String {
455        match self {
456            ApiType::Json => "application/json",
457            ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
458            ApiType::Txt => "text/plain",
459        }
460        .to_string()
461    }
462}