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