Skip to main content

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#[allow(clippy::too_many_arguments)]
12#[cfg(any(
13    feature = "sqlite",
14    feature = "mssql",
15    feature = "mysql",
16    feature = "pgsql"
17))]
18/// 数据表构造
19pub mod tables;
20/// 工具
21pub mod tools;
22
23use crate::action::Action;
24use crate::addon::Addon;
25use crate::module::Module;
26use crate::request::Request;
27use crate::tools::{Tools, ToolsConfig};
28#[cfg(any(
29    feature = "mysql",
30    feature = "sqlite",
31    feature = "mssql",
32    feature = "pgsql"
33))]
34use br_db::types::TableOptions;
35use json::{array, object, JsonValue};
36use lazy_static::lazy_static;
37use log::{error, info};
38use std::cell::RefCell;
39use std::collections::HashMap;
40use std::path::PathBuf;
41use std::sync::LazyLock;
42use std::sync::{Mutex, OnceLock};
43use std::{fs, thread};
44
45lazy_static! {
46    static ref CONFIG: Mutex<HashMap<String, JsonValue>> = Mutex::new(HashMap::new());
47}
48/// 工具集合(写入一次,后续只读)
49static PLUGIN_TOOLS: OnceLock<Tools> = OnceLock::new();
50/// 全局监听线程
51static GLOBAL_HANDLE: LazyLock<Mutex<HashMap<String, String>>> =
52    LazyLock::new(|| Mutex::new(HashMap::new()));
53/// 全局加载的插件清单
54pub static GLOBAL_ADDONS: OnceLock<Vec<String>> = OnceLock::new();
55/// 全局加载的模块清单
56pub static GLOBAL_MODULE: OnceLock<Vec<String>> = OnceLock::new();
57/// 全局加载的动作清单
58pub static GLOBAL_ACTION: OnceLock<Vec<String>> = OnceLock::new();
59
60thread_local! {
61    /// 单线程全局变量
62    static GLOBAL_DATA: RefCell<JsonValue> = RefCell::new(object!{});
63}
64/// 插件接口
65pub trait Plugin {
66    /// 加载插件
67    fn addon(name: &str) -> Result<Box<dyn Addon>, String>;
68    /// 加载模型
69    fn module(name: &str) -> Result<Box<dyn Module>, String> {
70        let (addon_name, module_name) = Self::split2(name)?;
71        Self::addon(addon_name)?.module(module_name)
72    }
73    /// 加载动作
74    fn action(name: &str) -> Result<Box<dyn Action>, String> {
75        let (addon_name, module_name, action_name) = Self::split3(name)?;
76        Self::addon(addon_name)?
77            .module(module_name)?
78            .action(action_name)
79    }
80    /// 内部api
81    fn api_run(name: &str, request: Request) -> Result<JsonValue, String> {
82        let (addon_name, module_name, action_name) = Self::split3(name)?;
83        match Self::addon(addon_name)?
84            .module(module_name)?
85            .action(action_name)?
86            .run(request)
87        {
88            Ok(e) => Ok(e.data),
89            Err(e) => Err(e.message),
90        }
91    }
92    /// 加载工具装置
93    /// * path 配置文件路径
94    fn load_tools(config: ToolsConfig) -> Result<(), String> {
95        if PLUGIN_TOOLS.get().is_some() {
96            return Ok(());
97        }
98        let tools = Tools::new(config)?;
99        let _ = PLUGIN_TOOLS.set(tools);
100        Ok(())
101    }
102    /// 获取工具
103    fn get_tools() -> Tools {
104        PLUGIN_TOOLS.get().expect("tools not initialized").clone()
105    }
106    /// 加载全局配置文件
107    fn load_config(name: &str, config: JsonValue) {
108        if let Ok(mut cfg) = CONFIG.lock() {
109            cfg.insert(name.into(), config.clone());
110        }
111    }
112    /// 根据API清单初始化数据库
113    ///
114    /// * path 插件目录
115    #[cfg(any(
116        feature = "mysql",
117        feature = "sqlite",
118        feature = "mssql",
119        feature = "pgsql"
120    ))]
121    fn init_db() -> Result<(), String> {
122        info!("=============数据库更新开始=============");
123        let mut tables = HashMap::new();
124        // 生成数据库文件
125        let sql = PathBuf::from("sql");
126
127        // 删除目录
128        if let Some(sql_path) = sql.to_str() {
129            match fs::remove_dir_all(sql_path) {
130                Ok(_) => {}
131                Err(e) => {
132                    #[cfg(any(
133                        feature = "mysql",
134                        feature = "sqlite",
135                        feature = "mssql",
136                        feature = "pgsql"
137                    ))]
138                    error!("目录删除失败: {e}");
139                }
140            }
141        }
142
143        // 创建目录
144        if let Some(sql_path) = sql.to_str() {
145            match fs::create_dir_all(sql_path) {
146                Ok(_) => {}
147                Err(e) => error!("目录创建失败: {e}"),
148            }
149        }
150
151        let sql_file = sql.join("sql.json");
152        let mut db_install = array![];
153
154        for api_name in GLOBAL_MODULE.wait() {
155            let module = match Self::module(api_name) {
156                Ok(e) => e,
157                Err(e) => {
158                    error!("加载模型错误: {}", e);
159                    continue;
160                }
161            };
162            if !module.table() {
163                continue;
164            }
165            if !tables.contains_key(module._table_name()) {
166                tables.insert(module._table_name(), module);
167            }
168        }
169
170        for (_, module) in tables.iter_mut() {
171            let mut opt = TableOptions::default();
172
173            let unique = module
174                .table_unique()
175                .iter()
176                .map(|x| (*x).to_string())
177                .collect::<Vec<String>>();
178            let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
179
180            let index = module
181                .table_index()
182                .iter()
183                .map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>())
184                .collect::<Vec<Vec<String>>>();
185            let index = index
186                .iter()
187                .map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>())
188                .collect::<Vec<Vec<&str>>>();
189
190            opt.set_table_name(module._table_name());
191            opt.set_table_title(module.title());
192            opt.set_table_key(module.table_key());
193            opt.set_table_fields(module.fields().clone());
194            opt.set_table_unique(unique.clone());
195            opt.set_table_index(index.clone());
196            opt.set_table_partition(module.table_partition());
197            opt.set_table_partition_columns(module.table_partition_columns());
198            let mut tools = Self::get_tools();
199            let sql = tools
200                .db
201                .table(module._table_name())
202                .fetch_sql()
203                .table_create(opt.clone());
204            let update_sql = tools
205                .db
206                .table(module._table_name())
207                .fetch_sql()
208                .table_update(opt.clone());
209            db_install
210                .push(object! {
211                   table:module._table_name(),
212                   field:module.fields().clone(),
213                   key:module.table_key(),
214                   index:index.clone(),
215                   unique:unique.clone(),
216                   sql:sql,
217                   update_sql:update_sql
218                })
219                .ok();
220            if let Err(e) = fs::write(sql_file.clone(), db_install.to_string()) {
221                error!("写入SQL文件失败: {e}");
222            }
223
224            let mut exec_tools = Self::get_tools();
225            if exec_tools.db.table_is_exist(module._table_name()) {
226                let res = exec_tools.db.table_update(opt);
227                match res.as_i32().unwrap_or(-1) {
228                    -1 => {}
229                    0 => {
230                        info!("数据库更新情况: {} 失败", module._table_name());
231                    }
232                    1 => {
233                        info!("数据库更新情况: {} 成功", module._table_name());
234                    }
235                    _ => {}
236                }
237            } else {
238                let res = exec_tools.db.table_create(opt);
239                info!("安装完成情况: {} {}", module._table_name(), res);
240            }
241            let init_data = module.init_data();
242            if !init_data.is_empty() {
243                let count = exec_tools.db.table(module._table_name()).count();
244                if count.is_empty() {
245                    let r = exec_tools
246                        .db
247                        .table(module._table_name())
248                        .insert_all(init_data);
249                    info!("初始化【{}】数据 {} 条", module._table_name(), r.len());
250                }
251            }
252        }
253        info!("=============数据库更新完成=============");
254        Ok(())
255    }
256
257    /// 全局插件监听入口
258    fn handles() {
259        let mut map = match GLOBAL_HANDLE.lock() {
260            Ok(m) => m,
261            Err(_) => return,
262        };
263
264        for api in GLOBAL_ADDONS.wait() {
265            let mut addon_name = match Self::addon(api.as_str()) {
266                Ok(e) => e,
267                Err(e) => {
268                    error!("插件: {api} 加载错误 {e}");
269                    continue;
270                }
271            };
272            if map.get(addon_name.name()).is_none() {
273                map.insert(addon_name.name().to_string(), addon_name.name().to_string());
274                thread::spawn(move || addon_name.handle());
275            }
276        }
277        for api in GLOBAL_MODULE.wait() {
278            let mut module_name = match Self::module(api.as_str()) {
279                Ok(e) => e,
280                Err(e) => {
281                    error!("插件: {api} 加载错误 {e}");
282                    continue;
283                }
284            };
285            if map.get(module_name.module_name()).is_none() {
286                map.insert(
287                    module_name.module_name().to_string(),
288                    module_name.module_name().to_string(),
289                );
290                thread::spawn(move || module_name.handle());
291            }
292        }
293    }
294    /// 生成Swagger
295    fn swagger(
296        title: &str,
297        description: &str,
298        version: &str,
299        uaturl: &str,
300        produrl: &str,
301        tags: JsonValue,
302        paths: JsonValue,
303    ) -> JsonValue {
304        let info = object! {
305            openapi:"3.0.0",
306            info:{
307                title:title,
308                description:description,
309                version:version
310            },
311            components: {
312                securitySchemes: {
313                    BearerToken: {
314                        "type": "http",
315                        "scheme": "bearer",
316                        "bearerFormat": "Token"
317                    }
318                }
319            },
320            tags:tags,
321            security: [
322                {
323                    "BearerToken": []
324                }
325            ],
326            servers:[
327                {
328                    "url":uaturl,
329                    "description": "测试地址"
330                },
331                {
332                    "url":produrl,
333                    "description": "正式地址"
334                }
335            ],
336            paths:paths
337        };
338        info
339    }
340    /// 生成 api 列表
341    fn generate_api_list(
342        apipath: PathBuf,
343        path: PathBuf,
344        index: usize,
345    ) -> Result<Vec<String>, String> {
346        #[cfg(debug_assertions)]
347        {
348            let mut plugin_list = Vec::new();
349            if path.is_dir() {
350                let res = fs::read_dir(path);
351                match res {
352                    Ok(entries) => {
353                        for entry in entries {
354                            let entry = match entry {
355                                Ok(e) => e,
356                                Err(e) => return Err(e.to_string()),
357                            };
358                            let path = entry.path();
359                            if path.is_dir() {
360                                let path_str = match path.to_str() {
361                                    Some(s) => s,
362                                    None => continue,
363                                };
364                                let res = Self::generate_api_list(
365                                    apipath.clone(),
366                                    path_str.parse().unwrap_or_default(),
367                                    index + 1,
368                                )?;
369                                plugin_list.extend(res);
370                            } else if path.is_file() {
371                                let path_str = match path.to_str() {
372                                    Some(s) => s,
373                                    None => continue,
374                                };
375                                if path_str.ends_with("mod.rs") {
376                                    continue;
377                                }
378                                let addon = path
379                                    .parent()
380                                    .and_then(|p| p.parent())
381                                    .and_then(|p| p.file_name())
382                                    .and_then(|n| n.to_str())
383                                    .unwrap_or_default();
384                                let model = path
385                                    .parent()
386                                    .and_then(|p| p.file_name())
387                                    .and_then(|n| n.to_str())
388                                    .unwrap_or_default();
389                                let action = path
390                                    .file_name()
391                                    .and_then(|n| n.to_str())
392                                    .unwrap_or_default()
393                                    .trim_end_matches(".rs");
394                                let api = format!("{addon}.{model}.{action}");
395                                match Self::action(api.as_str()) {
396                                    Ok(e) => plugin_list.push(e.api()),
397                                    Err(_) => continue,
398                                }
399                            }
400                        }
401                    }
402                    Err(e) => return Err(e.to_string()),
403                }
404            }
405            if index == 0 {
406                if let Some(parent) = apipath.clone().parent() {
407                    let _ = fs::create_dir_all(parent);
408                }
409                let _ = fs::write(&apipath, JsonValue::from(plugin_list.clone()).to_string());
410                info!(
411                    "=============API数量: {} 条=============",
412                    plugin_list.len()
413                );
414                Self::_load_apis(plugin_list.clone())?;
415            }
416            Ok(plugin_list)
417        }
418        #[cfg(not(debug_assertions))]
419        {
420            let apis = fs::read_to_string(&apipath).unwrap_or_default();
421            let apis = json::parse(&apis).unwrap_or(array![]);
422            let apis = apis
423                .members()
424                .map(|x| x.as_str().unwrap_or_default().to_string())
425                .collect::<Vec<String>>();
426            info!("=============API数量: {} 条=============", apis.len());
427            Self::_load_apis(apis.clone())?;
428            Ok(apis)
429        }
430    }
431    /// 加载api清单
432    fn _load_apis(apis: Vec<String>) -> Result<(), String> {
433        let mut action_list = vec![];
434        let mut module_list = vec![];
435        let mut addons_list = vec![];
436        for api in apis {
437            let action = Self::action(api.as_str())?;
438            action_list.push(action.api());
439            if !module_list.contains(&action.module_name()) {
440                module_list.push(action.module_name());
441            }
442            if !addons_list.contains(&action.addon_name()) {
443                addons_list.push(action.addon_name());
444            }
445        }
446        let _ = GLOBAL_ACTION.set(action_list);
447        let _ = GLOBAL_MODULE.set(module_list);
448        let _ = GLOBAL_ADDONS.set(addons_list);
449        Ok(())
450    }
451    /// 设置全局变量
452    fn set_global_data(key: &str, value: JsonValue) {
453        GLOBAL_DATA.with(|data| {
454            data.borrow_mut()[key] = value;
455        });
456    }
457    /// 获取全局变量数据
458    fn get_global_data() -> JsonValue {
459        GLOBAL_DATA.with(|data| data.borrow().clone())
460    }
461    /// 获取全局变量指定字段数据
462    fn get_global_data_key(key: &str) -> JsonValue {
463        GLOBAL_DATA.with(|data| data.borrow()[key].clone())
464    }
465    #[inline]
466    fn split2(name: &str) -> Result<(&str, &str), String> {
467        let t = name.split('.').collect::<Vec<&str>>();
468        if t.len() < 2 {
469            return Err(format!("模型格式不正确: {name}"));
470        }
471        Ok((t[0], t[1]))
472    }
473
474    #[inline]
475    fn split3(name: &str) -> Result<(&str, &str, &str), String> {
476        if let Some((a, rest)) = name.split_once('.') {
477            if let Some((b, c)) = rest.split_once('.') {
478                if !a.is_empty() && !b.is_empty() && !c.is_empty() {
479                    Ok((a, b, c))
480                } else {
481                    Err("动作格式不正确".to_string())
482                }
483            } else {
484                Err("动作格式不正确".to_string())
485            }
486        } else {
487            Err("动作格式不正确".to_string())
488        }
489    }
490}
491/// API 错误响应
492#[derive(Debug, Clone)]
493pub struct ApiResponse {
494    pub types: ApiType,
495    pub code: i32,
496    pub message: String,
497    pub data: JsonValue,
498    pub success: bool,
499    pub timestamp: i64,
500}
501impl ApiResponse {
502    #[must_use]
503    pub fn json(&self) -> JsonValue {
504        match self.types {
505            ApiType::Json => object! {
506                code: self.code,
507                message: self.message.clone(),
508                data: self.data.clone(),
509                success: self.success
510            },
511            ApiType::Redirect
512            | ApiType::Download
513            | ApiType::Preview
514            | ApiType::Txt
515            | ApiType::Html => self.data.clone(),
516        }
517    }
518    pub fn swagger(&mut self) -> JsonValue {
519        let type_str = self.types.str();
520        let mut content = object! {};
521        content[type_str] = object! {};
522        content[type_str]["schema"]["type"] = if self.data.is_array() {
523            "array"
524        } else {
525            "object"
526        }
527        .into();
528        content[type_str]["schema"]["properties"] = self.data.clone();
529
530        content[type_str]["schema"]["type"] = match content[type_str]["schema"]["type"]
531            .as_str()
532            .unwrap_or("object")
533        {
534            "int" => "integer".into(),
535            _ => content[type_str]["schema"]["type"].clone(),
536        };
537        object! {
538            "description":self.message.clone(),
539            "content":content
540        }
541    }
542    pub fn success(data: JsonValue, mut message: &str) -> Self {
543        if message.is_empty() {
544            message = "success";
545        }
546        Self {
547            success: true,
548            types: ApiType::Json,
549            code: 0,
550            message: message.to_string(),
551            data,
552            timestamp: br_fields::datetime::Timestamp::timestamp(),
553        }
554    }
555    pub fn fail(code: i32, message: &str) -> Self {
556        Self {
557            types: ApiType::Json,
558            code,
559            message: message.to_string(),
560            data: JsonValue::Null,
561            success: false,
562            timestamp: br_fields::datetime::Timestamp::timestamp(),
563        }
564    }
565    pub fn error(data: JsonValue, message: &str) -> Self {
566        Self {
567            types: ApiType::Json,
568            code: -1,
569            message: message.to_string(),
570            data,
571            success: false,
572            timestamp: br_fields::datetime::Timestamp::timestamp(),
573        }
574    }
575    /// 重定向
576    pub fn redirect(url: &str) -> Self {
577        Self {
578            types: ApiType::Redirect,
579            code: 0,
580            message: "".to_string(),
581            data: url.into(),
582            success: true,
583            timestamp: br_fields::datetime::Timestamp::timestamp(),
584        }
585    }
586    /// 下载
587    pub fn download(filename: &str) -> Self {
588        Self {
589            types: ApiType::Download,
590            code: 0,
591            message: "".to_string(),
592            data: filename.into(),
593            success: true,
594            timestamp: br_fields::datetime::Timestamp::timestamp(),
595        }
596    }
597    /// 预览
598    pub fn preview(filename: &str) -> Self {
599        Self {
600            types: ApiType::Preview,
601            code: 0,
602            message: "".to_string(),
603            data: filename.into(),
604            success: true,
605            timestamp: br_fields::datetime::Timestamp::timestamp(),
606        }
607    }
608    /// 文本
609    pub fn txt(txt: &str) -> Self {
610        Self {
611            types: ApiType::Txt,
612            code: 0,
613            message: "".to_string(),
614            data: txt.into(),
615            success: true,
616            timestamp: br_fields::datetime::Timestamp::timestamp(),
617        }
618    }
619    pub fn html(data: &str) -> Self {
620        Self {
621            types: ApiType::Html,
622            code: 0,
623            message: "".to_string(),
624            data: data.into(),
625            success: true,
626            timestamp: br_fields::datetime::Timestamp::timestamp(),
627        }
628    }
629}
630impl Default for ApiResponse {
631    fn default() -> Self {
632        Self {
633            types: ApiType::Json,
634            code: 0,
635            message: "".to_string(),
636            data: JsonValue::Null,
637            success: false,
638            timestamp: br_fields::datetime::Timestamp::timestamp(),
639        }
640    }
641}
642/// API 响应
643#[derive(Debug, Clone)]
644pub enum ApiType {
645    /// JSON类型
646    Json,
647    /// 重定向
648    /// (重定向地址: http://xxxxxx)
649    Redirect,
650    /// 下载
651    /// (文件地址: 文件绝对地址)
652    Download,
653    /// 预览
654    /// (文件地址: 文件绝对地址)
655    Preview,
656    /// TXT格式
657    Txt,
658    /// 返回网页
659    Html,
660}
661impl ApiType {
662    #[must_use]
663    pub fn str(&self) -> &'static str {
664        match self {
665            Self::Json => "application/json",
666            Self::Redirect | Self::Download | Self::Preview => "text/html",
667            Self::Txt => "text/plain",
668            Self::Html => "text/html",
669        }
670    }
671}