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