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
205            let mut exec_tools = Self::get_tools();
206            let table_exists = exec_tools.db.table_is_exist(module._table_name());
207
208            let update_sql = if table_exists {
209                tools
210                    .db
211                    .table(module._table_name())
212                    .fetch_sql()
213                    .table_update(opt.clone())
214            } else {
215                JsonValue::Null
216            };
217            db_install
218                .push(object! {
219                   table:module._table_name(),
220                   field:module.fields().clone(),
221                   key:module.table_key(),
222                   index:index.clone(),
223                   unique:unique.clone(),
224                   sql:sql,
225                   update_sql:update_sql
226                })
227                .ok();
228            if let Err(e) = fs::write(sql_file.clone(), db_install.to_string()) {
229                error!("写入SQL文件失败: {e}");
230            }
231
232            if table_exists {
233                let res = exec_tools.db.table_update(opt);
234                match res.as_i32().unwrap_or(-1) {
235                    -1 => {}
236                    0 => {
237                        info!("数据库更新情况: {} 失败", module._table_name());
238                    }
239                    1 => {
240                        info!("数据库更新情况: {} 成功", module._table_name());
241                    }
242                    _ => {}
243                }
244            } else {
245                let res = exec_tools.db.table_create(opt);
246                info!("安装完成情况: {} {}", module._table_name(), res);
247            }
248            let init_data = module.init_data();
249            if !init_data.is_empty() {
250                let count = exec_tools.db.table(module._table_name()).count();
251                if count.is_empty() {
252                    let r = exec_tools
253                        .db
254                        .table(module._table_name())
255                        .insert_all(init_data);
256                    info!("初始化【{}】数据 {} 条", module._table_name(), r.len());
257                }
258            }
259        }
260        info!("=============数据库更新完成=============");
261        Ok(())
262    }
263
264    /// 全局插件监听入口
265    fn handles() {
266        let mut map = match GLOBAL_HANDLE.lock() {
267            Ok(m) => m,
268            Err(_) => return,
269        };
270
271        for api in GLOBAL_ADDONS.wait() {
272            let mut addon_name = match Self::addon(api.as_str()) {
273                Ok(e) => e,
274                Err(e) => {
275                    error!("插件: {api} 加载错误 {e}");
276                    continue;
277                }
278            };
279            if map.get(addon_name.name()).is_none() {
280                map.insert(addon_name.name().to_string(), addon_name.name().to_string());
281                thread::spawn(move || addon_name.handle());
282            }
283        }
284        for api in GLOBAL_MODULE.wait() {
285            let mut module_name = match Self::module(api.as_str()) {
286                Ok(e) => e,
287                Err(e) => {
288                    error!("插件: {api} 加载错误 {e}");
289                    continue;
290                }
291            };
292            if map.get(module_name.module_name()).is_none() {
293                map.insert(
294                    module_name.module_name().to_string(),
295                    module_name.module_name().to_string(),
296                );
297                thread::spawn(move || module_name.handle());
298            }
299        }
300    }
301    /// 生成Swagger
302    fn swagger(
303        title: &str,
304        description: &str,
305        version: &str,
306        uaturl: &str,
307        produrl: &str,
308        tags: JsonValue,
309        paths: JsonValue,
310    ) -> JsonValue {
311        let info = object! {
312            openapi:"3.0.0",
313            info:{
314                title:title,
315                description:description,
316                version:version
317            },
318            components: {
319                securitySchemes: {
320                    BearerToken: {
321                        "type": "http",
322                        "scheme": "bearer",
323                        "bearerFormat": "Token"
324                    }
325                }
326            },
327            tags:tags,
328            security: [
329                {
330                    "BearerToken": []
331                }
332            ],
333            servers:[
334                {
335                    "url":uaturl,
336                    "description": "测试地址"
337                },
338                {
339                    "url":produrl,
340                    "description": "正式地址"
341                }
342            ],
343            paths:paths
344        };
345        info
346    }
347    /// 生成 api 列表
348    fn generate_api_list(
349        apipath: PathBuf,
350        path: PathBuf,
351        index: usize,
352    ) -> Result<Vec<String>, String> {
353        #[cfg(debug_assertions)]
354        {
355            let mut plugin_list = Vec::new();
356            if path.is_dir() {
357                let res = fs::read_dir(path);
358                match res {
359                    Ok(entries) => {
360                        for entry in entries {
361                            let entry = match entry {
362                                Ok(e) => e,
363                                Err(e) => return Err(e.to_string()),
364                            };
365                            let path = entry.path();
366                            if path.is_dir() {
367                                let path_str = match path.to_str() {
368                                    Some(s) => s,
369                                    None => continue,
370                                };
371                                let res = Self::generate_api_list(
372                                    apipath.clone(),
373                                    path_str.parse().unwrap_or_default(),
374                                    index + 1,
375                                )?;
376                                plugin_list.extend(res);
377                            } else if path.is_file() {
378                                let path_str = match path.to_str() {
379                                    Some(s) => s,
380                                    None => continue,
381                                };
382                                if path_str.ends_with("mod.rs") {
383                                    continue;
384                                }
385                                let addon = path
386                                    .parent()
387                                    .and_then(|p| p.parent())
388                                    .and_then(|p| p.file_name())
389                                    .and_then(|n| n.to_str())
390                                    .unwrap_or_default();
391                                let model = path
392                                    .parent()
393                                    .and_then(|p| p.file_name())
394                                    .and_then(|n| n.to_str())
395                                    .unwrap_or_default();
396                                let action = path
397                                    .file_name()
398                                    .and_then(|n| n.to_str())
399                                    .unwrap_or_default()
400                                    .trim_end_matches(".rs");
401                                let api = format!("{addon}.{model}.{action}");
402                                match Self::action(api.as_str()) {
403                                    Ok(e) => plugin_list.push(e.api()),
404                                    Err(_) => continue,
405                                }
406                            }
407                        }
408                    }
409                    Err(e) => return Err(e.to_string()),
410                }
411            }
412            if index == 0 {
413                if let Some(parent) = apipath.clone().parent() {
414                    let _ = fs::create_dir_all(parent);
415                }
416                let _ = fs::write(&apipath, JsonValue::from(plugin_list.clone()).to_string());
417                info!(
418                    "=============API数量: {} 条=============",
419                    plugin_list.len()
420                );
421                Self::_load_apis(plugin_list.clone())?;
422            }
423            Ok(plugin_list)
424        }
425        #[cfg(not(debug_assertions))]
426        {
427            let apis = fs::read_to_string(&apipath).unwrap_or_default();
428            let apis = json::parse(&apis).unwrap_or(array![]);
429            let apis = apis
430                .members()
431                .map(|x| x.as_str().unwrap_or_default().to_string())
432                .collect::<Vec<String>>();
433            info!("=============API数量: {} 条=============", apis.len());
434            Self::_load_apis(apis.clone())?;
435            Ok(apis)
436        }
437    }
438    /// 加载api清单
439    fn _load_apis(apis: Vec<String>) -> Result<(), String> {
440        let mut action_list = vec![];
441        let mut module_list = vec![];
442        let mut addons_list = vec![];
443        for api in apis {
444            let action = Self::action(api.as_str())?;
445            action_list.push(action.api());
446            if !module_list.contains(&action.module_name()) {
447                module_list.push(action.module_name());
448            }
449            if !addons_list.contains(&action.addon_name()) {
450                addons_list.push(action.addon_name());
451            }
452        }
453        let _ = GLOBAL_ACTION.set(action_list);
454        let _ = GLOBAL_MODULE.set(module_list);
455        let _ = GLOBAL_ADDONS.set(addons_list);
456        Ok(())
457    }
458    /// 设置全局变量
459    fn set_global_data(key: &str, value: JsonValue) {
460        GLOBAL_DATA.with(|data| {
461            data.borrow_mut()[key] = value;
462        });
463    }
464    /// 获取全局变量数据
465    fn get_global_data() -> JsonValue {
466        GLOBAL_DATA.with(|data| data.borrow().clone())
467    }
468    /// 获取全局变量指定字段数据
469    fn get_global_data_key(key: &str) -> JsonValue {
470        GLOBAL_DATA.with(|data| data.borrow()[key].clone())
471    }
472    #[inline]
473    fn split2(name: &str) -> Result<(&str, &str), String> {
474        let t = name.split('.').collect::<Vec<&str>>();
475        if t.len() < 2 {
476            return Err(format!("模型格式不正确: {name}"));
477        }
478        Ok((t[0], t[1]))
479    }
480
481    #[inline]
482    fn split3(name: &str) -> Result<(&str, &str, &str), String> {
483        if let Some((a, rest)) = name.split_once('.') {
484            if let Some((b, c)) = rest.split_once('.') {
485                if !a.is_empty() && !b.is_empty() && !c.is_empty() {
486                    Ok((a, b, c))
487                } else {
488                    Err("动作格式不正确".to_string())
489                }
490            } else {
491                Err("动作格式不正确".to_string())
492            }
493        } else {
494            Err("动作格式不正确".to_string())
495        }
496    }
497}
498/// API 错误响应
499#[derive(Debug, Clone)]
500pub struct ApiResponse {
501    pub types: ApiType,
502    pub code: i32,
503    pub message: String,
504    pub data: JsonValue,
505    pub success: bool,
506    pub timestamp: i64,
507}
508impl ApiResponse {
509    #[must_use]
510    pub fn json(&self) -> JsonValue {
511        match self.types {
512            ApiType::Json => object! {
513                code: self.code,
514                message: self.message.clone(),
515                data: self.data.clone(),
516                success: self.success
517            },
518            ApiType::Redirect
519            | ApiType::Download
520            | ApiType::Preview
521            | ApiType::Txt
522            | ApiType::Html => self.data.clone(),
523        }
524    }
525    pub fn swagger(&mut self) -> JsonValue {
526        let type_str = self.types.str();
527        let mut content = object! {};
528        content[type_str] = object! {};
529        content[type_str]["schema"]["type"] = if self.data.is_array() {
530            "array"
531        } else {
532            "object"
533        }
534        .into();
535        content[type_str]["schema"]["properties"] = self.data.clone();
536
537        content[type_str]["schema"]["type"] = match content[type_str]["schema"]["type"]
538            .as_str()
539            .unwrap_or("object")
540        {
541            "int" => "integer".into(),
542            _ => content[type_str]["schema"]["type"].clone(),
543        };
544        object! {
545            "description":self.message.clone(),
546            "content":content
547        }
548    }
549    pub fn success(data: JsonValue, mut message: &str) -> Self {
550        if message.is_empty() {
551            message = "success";
552        }
553        Self {
554            success: true,
555            types: ApiType::Json,
556            code: 0,
557            message: message.to_string(),
558            data,
559            timestamp: br_fields::datetime::Timestamp::timestamp(),
560        }
561    }
562    pub fn fail(code: i32, message: &str) -> Self {
563        Self {
564            types: ApiType::Json,
565            code,
566            message: message.to_string(),
567            data: JsonValue::Null,
568            success: false,
569            timestamp: br_fields::datetime::Timestamp::timestamp(),
570        }
571    }
572    pub fn error(data: JsonValue, message: &str) -> Self {
573        Self {
574            types: ApiType::Json,
575            code: -1,
576            message: message.to_string(),
577            data,
578            success: false,
579            timestamp: br_fields::datetime::Timestamp::timestamp(),
580        }
581    }
582    /// 重定向
583    pub fn redirect(url: &str) -> Self {
584        Self {
585            types: ApiType::Redirect,
586            code: 0,
587            message: "".to_string(),
588            data: url.into(),
589            success: true,
590            timestamp: br_fields::datetime::Timestamp::timestamp(),
591        }
592    }
593    /// 下载
594    pub fn download(filename: &str) -> Self {
595        Self {
596            types: ApiType::Download,
597            code: 0,
598            message: "".to_string(),
599            data: filename.into(),
600            success: true,
601            timestamp: br_fields::datetime::Timestamp::timestamp(),
602        }
603    }
604    /// 预览
605    pub fn preview(filename: &str) -> Self {
606        Self {
607            types: ApiType::Preview,
608            code: 0,
609            message: "".to_string(),
610            data: filename.into(),
611            success: true,
612            timestamp: br_fields::datetime::Timestamp::timestamp(),
613        }
614    }
615    /// 文本
616    pub fn txt(txt: &str) -> Self {
617        Self {
618            types: ApiType::Txt,
619            code: 0,
620            message: "".to_string(),
621            data: txt.into(),
622            success: true,
623            timestamp: br_fields::datetime::Timestamp::timestamp(),
624        }
625    }
626    pub fn html(data: &str) -> Self {
627        Self {
628            types: ApiType::Html,
629            code: 0,
630            message: "".to_string(),
631            data: data.into(),
632            success: true,
633            timestamp: br_fields::datetime::Timestamp::timestamp(),
634        }
635    }
636}
637impl Default for ApiResponse {
638    fn default() -> Self {
639        Self {
640            types: ApiType::Json,
641            code: 0,
642            message: "".to_string(),
643            data: JsonValue::Null,
644            success: false,
645            timestamp: br_fields::datetime::Timestamp::timestamp(),
646        }
647    }
648}
649/// API 响应
650#[derive(Debug, Clone)]
651pub enum ApiType {
652    /// JSON类型
653    Json,
654    /// 重定向
655    /// (重定向地址: http://xxxxxx)
656    Redirect,
657    /// 下载
658    /// (文件地址: 文件绝对地址)
659    Download,
660    /// 预览
661    /// (文件地址: 文件绝对地址)
662    Preview,
663    /// TXT格式
664    Txt,
665    /// 返回网页
666    Html,
667}
668impl ApiType {
669    #[must_use]
670    pub fn str(&self) -> &'static str {
671        match self {
672            Self::Json => "application/json",
673            Self::Redirect | Self::Download | Self::Preview => "text/html",
674            Self::Txt => "text/plain",
675            Self::Html => "text/html",
676        }
677    }
678}