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