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